module scene.game.actor;

import std.algorithm, std.datetime, std.format;

import mirdefine, mirshare, scene.game.mirmap, scene.game.magic;
import common.imagearray, common.imagehandler, common.sprite;
import share.container, share.types, share.protocol, share.utility, share.edcode;

import allegro5.allegro;
import allegro5.allegro_primitives;




void assignTargetRegion(in IntRectangle actor, ref IntRectangle target)
{
    if ( ( actor.getWidth() ) > _CELL_WIDTH + _TARGETRGN_GAPX )
    {
        target.setLeft( actor.getLeft() + ( (actor.getWidth()) - (_CELL_WIDTH + _TARGETRGN_GAPX) )/2 );
        target.setRight( actor.getLeft() + ( (actor.getWidth()) + (_CELL_WIDTH + _TARGETRGN_GAPX) )/2 );
    }
    else
    {
        target.setLeft( actor.getLeft() );
        target.setRight( actor.getRight() );
    }

    if ( ( actor.getHeight() ) > _CELL_HEIGHT + _TARGETRGN_GAPY )
    {
        target.setTop( actor.getTop() + ( (actor.getHeight()) - (_CELL_HEIGHT + _TARGETRGN_GAPY) )/2 );
        target.setBottom( actor.getTop() + ( (actor.getHeight()) + (_CELL_HEIGHT + _TARGETRGN_GAPY) )/2 );
    }
    else
    {
        target.setTop( actor.getTop() );
        target.setBottom( actor.getBottom() );
    }
}



struct Feature
{
    public ubyte Gender;
    public ubyte Weapon;
    public ubyte Dress;
    public ubyte Hair;
}
alias Feature* PFeature;

public class Actor
{
    private alias Queue!PPacketMsg PacketQueue;

    public uint m_dwIdentity;                   // ID Index From Server.
    public string m_szName;                     // Ä³¸¯ÅÍ³ª ¸ó½ºÅÍÀÇ ÀÌ¸§.
    public string m_szGuildName;
    
    public Feature m_stFeature;                 // Ä³¸¯ÅÍÀÇ ¿ÜÇüÀ» °áÁ¤Áþ´Â´Ù.
    public ubyte m_bActorImgIdx;                // ÇöÀç Actor°¡ °¡Áú ÀÌ¹ÌÁö±¸Á¶Ã¼ÀÇ ¹øÈ£.
    public ubyte m_bEffectImgIdx;               // ÇöÀç Actor°¡ °¡Áú ÀÌ¹ÌÁö±¸Á¶Ã¼ÀÇ ¹øÈ£.
    public ImageArray m_pxActorImage;           // ÀÌ¹ÌÁö¸¦ ¾ò¾î¿Ã Æ÷ÀÎÅÍµé.
    public IntRectangle m_rcActor;              // ÇöÀç ½ºÅ©¸°»óÀÇActorÀÇ »ç°¢ ¿µ¿ª.
    public IntRectangle m_rcTargetRgn;          // ÇöÀç ActorÀÇ Å¸°Ù¿µ¿ª.
    
    public uint m_dwFstFrame;                   // ÇöÀç µ¿ÀÛÀÇ ½ÃÀÛ ÇÁ·¹ÀÓ.
    public uint m_dwEndFrame;                   // ÇöÀç µ¿ÀÛÀÇ ¸¶Áö¸·ÇÁ·¹ÀÓ.
    public ushort m_wDelay;                     // ÇöÀç µ¿ÀÛ¿¡ ´ëÇÑ Áö¿¬½Ã°£.
    
    public ubyte m_bCurrMtn;                    // ÇöÀç µ¿ÀÛ.
    public ubyte m_bCurrDir;                    // ÇöÀç ¹æÇâ.
    public ubyte m_bMoveDir;                    // ÇöÀç ¹æÇâ.
    public uint m_dwCurrFrame;                  // ÇöÀç ÇÁ·¹ÀÓ.
    public ushort m_wCurrDelay;                 // ÇöÀç Áö¿¬½Ã°£.
    
    public ushort m_wOldPosX;                   // ÇöÀç Çàµ¿ÀÌ ÀÖ±âÀüÀÇ Å¸ÀÏXÁÂÇ¥.
    public ushort m_wOldPosY;
    public ubyte m_bOldDir;                     // ÇöÀç Çàµ¿ÀÌ ÀÖ±âÀüÀÇ ¹æÇâ.
    public ushort m_wPosX;                      // Å¸ÀÏ XÁÂÇ¥.
    public ushort m_wPosY;                      // Å¸ÀÏ YÁÂÇ¥.
    public int m_shShiftPixelX;                 // ÇöÀç ÀÚ½ÅÀÇ Å¸ÀÏ¿¡¼­ XÃà¸¸Å­ ÀÌµ¿ÇÑ ÇÈ¼¿ÀÇ °Å¸®.
    public int m_shShiftPixelY;                 // ÇöÀç ÀÚ½ÅÀÇ Å¸ÀÏ¿¡¼­ YÃà¸¸Å­ ÀÌµ¿ÇÑ ÇÈ¼¿ÀÇ °Å¸®.
    public int m_shShiftTileX;                  // ÇöÀç ÀÚ½ÅÀÇ Å¸ÀÏ¿¡¼­ XÃà¸¸Å­ ÀÌµ¿ÇÑ Å¸ÀÏÀÇ °Å¸®.
    public int m_shShiftTileY;                  // ÇöÀç ÀÚ½ÅÀÇ Å¸ÀÏ¿¡¼­ YÃà¸¸Å­ ÀÌµ¿ÇÑ Å¸ÀÏÀÇ °Å¸®.
    public int m_shScrnPosX;                    // Áß½ÉÁ¡À» °í·ÁÇÏÁö ¾ÊÀº È­¸é»óÀÇ XÁÂÇ¥.
    public int m_shScrnPosY;                    // Áß½ÉÁ¡À» °í·ÁÇÏÁö ¾ÊÀº È­¸é»óÀÇ YÁÂÇ¥.
    public ubyte m_bMoveSpeed;                  // ÀÌµ¿¼Óµµ.
    
    public PacketQueue m_xPacketQueue;                 // Actor°¡ Ã³¸®ÇØ¾ßµÉ ¸Þ½ÃÁö¸¦ ÀúÀåÇÏ°í ÀÖ´Â Å¥.
    public bool m_bMsgHurryCheck;               // ¸Þ½ÃÁö°¡ 2°³ÀÌ»ó ½×¿©ÀÖÀ»¶§´Â ÇÁ·¹ÀÓÀ» 1ÇÁ·¹ÀÓ¾¿À» °Ç³Ê¶Ú´Ù.
    
    public ubyte m_bBackStepFrame;
    public ubyte m_bBackStepFrameCnt;
    
    public uint m_dwCurrEffectFrame;            // Ä³¸¯ÅÍ EffectÇöÀç ÇÁ·¹ÀÓ.
    public uint m_dwFstEffectFrame;             // Ä³¸¯ÅÍ EffectÃ³À½ ÇÁ·¹ÀÓ.
    public uint m_dwEndEffectFrame;             // Ä³¸¯ÅÍ Effect¸¶Áö¸· ÇÁ·¹ÀÓ.
    public ubyte m_bEffectFrame;
    public ubyte m_bEffectFrameCnt;
    public ubyte m_bLightRadius[2];             // ±¤¿ø ³Êºñ.
    public ubyte m_bLightColor[2][3];           // ±¤¿ø »ö.
    
    public bool m_bUseEffect;                   // ÀÌÆåÆ®ÇÁ·¹ÀÓÀ» »ç¿ë.
    public bool m_bUseSwordEffect;              // °Ë¹ýÈ¿°ú »ç¿ë.
    
    public bool m_bWarMode;
    public uint m_dwWarModeTime;
    
    public ubyte m_bHPPercent;
    public ushort m_wMAXHP;
    public ushort m_wHP;
    public ushort m_wMP;
    public bool m_bOpenHealth;
    
    public ubyte m_bLightSize;                  // ActorÁÖº¯ÀÇ ±¤¿øÅ©±â.
    
    //D3DVERTEX m_avBoard[4];
    
    public ushort m_wABlendDelay;
    public ushort m_wABlendCurrDelay;
    public bool m_bABlendRev;
    
    public bool m_bReverse;
    
    public int m_nState;
    public ushort m_wStateClr;
    
    public ALLEGRO_COLOR m_dwNameClr;
    
    public bool m_bIsDead;
    
    public Feature m_stHitter;
    
    public ubyte m_bAppearState;
    
    public ubyte m_bMoveNextFrmCnt;
    public bool m_bIsMoved;
    
    public bool m_bFstSoundPlayed;
    
    public int m_nDividedChatLine;
    public uint m_wCurrChatDelay;
    public string m_szChatMsg;                  // Ã¤ÆÃ¸Þ½ÃÁö.
    public string m_szChatMsgArg[5];            // Ã¤ÆÃ¸Þ½ÃÁö¸¦ 5°³ÀÇ ½ºÆ®¸µÀ¸·Î ±¸ºÐÇÑ°Í.
    

    //---------------------------------------------------------------------------------------------------------------//
    // »ý¼ºÀÚ ¹× ¼Ò¸êÀÚ.
    public this()
    {
        InitActor();
    }

    public ~this()
    {
        DestroyActor();
    }
    
    public void InitActor()
    {
        m_wOldPosX          = 0;
        m_wOldPosY          = 0;
        m_bOldDir           = 0;
        
        m_bOpenHealth       = false;
        m_bWarMode          = false;
        m_bCurrMtn          = _MT_MON_STAND;
        m_bCurrDir          = _DIRECTION_LIST_1;
        m_bMoveDir          = _DIRECTION_LIST_1;
        m_bEffectFrameCnt   = _DEFAULT_SPELLFRAME;
        m_bHPPercent        = 100;
        
        m_bMoveNextFrmCnt   = 5;
        m_bIsMoved          = false;
        
        m_dwWarModeTime     = 0;
        m_wMAXHP            = 0;
        m_wHP               = 0;
        m_wMP               = 0;
        m_wPosX             = 0;
        m_wPosY             = 0;
        m_bLightSize        = 2;
        m_shShiftPixelX     = 0;
        m_shShiftPixelY     = 0;
        m_shShiftTileX      = 0;
        m_shShiftTileY      = 0;
        m_bMoveSpeed        = 0;
        m_shScrnPosX        = 0;
        m_shScrnPosY        = 0;
        m_wCurrDelay        = 0;
        m_dwFstFrame        = 0;
        m_dwEndFrame        = 1;
        m_dwCurrFrame       = 0;    
        m_wDelay            = 0;        
        m_pxActorImage      = null;
        m_bMsgHurryCheck    = false;
        m_bUseEffect        = false;
        m_bUseSwordEffect   = false;
        m_bReverse          = false;
        m_bIsDead           = false;
        m_bABlendRev        = false;
        m_dwCurrEffectFrame = 0;
        m_dwFstEffectFrame  = 0;
        m_dwEndEffectFrame  = 0;
        m_bEffectFrame      = 0;
        m_bBackStepFrame    = 0;
        m_bBackStepFrameCnt = 0;
        m_nState            = 0;
        m_nDividedChatLine  = 0;
        m_dwIdentity        = 0;
        m_wCurrChatDelay    = 0;
        m_wABlendCurrDelay  = 0;
        m_wABlendCurrDelay  = 0;
        
        m_bFstSoundPlayed   = false;
        
        m_bAppearState      = _DIG_NORMAL;
        
        m_wStateClr         = _STATE_NOTUSED;
        m_dwNameClr         = al_map_rgba(255, 255, 255, 255);
        
        m_bActorImgIdx      = _IMAGE_M_HUMAN;
        m_bEffectImgIdx     = _IMAGE_MAGIC;
        
        m_szName            = m_szName.init;
        m_stFeature         = Feature.init;
        m_stHitter          = Feature.init;
        m_bLightRadius      = m_bLightRadius.init;
        m_bLightColor       = m_bLightColor.init;
        m_szChatMsg         = m_szChatMsg.init;
        m_szChatMsgArg      = m_szChatMsgArg.init;
        m_rcActor           = IntRectangle.zero;
        m_rcTargetRgn       = IntRectangle.zero;
        
        /*D3DVECTOR       vNorm(0, 0, -1);
        m_avBoard[0] = D3DVERTEX(D3DVECTOR(-0.5f, 0.5f, 0), vNorm, 0, 0);
        m_avBoard[1] = D3DVERTEX(D3DVECTOR(-0.5f,-0.5f, 0), vNorm, 0, 1);
        m_avBoard[2] = D3DVERTEX(D3DVECTOR( 0.5f, 0.5f, 0), vNorm, 1, 0);
        m_avBoard[3] = D3DVERTEX(D3DVECTOR( 0.5f,-0.5f, 0), vNorm, 1, 1);*/
    }

    public void DestroyActor()
    {
        int nCnt, nLeftMsgCnt;
        PPacketMsg packet;
        
        packet  = null;
        nLeftMsgCnt = m_xPacketQueue.count();
        
        // ½×¿©ÀÖ´Â ÆÐÅ¶À» Áö¿î´Ù.
        if ( nLeftMsgCnt > 0 )
        {
            for ( nCnt = 0; nCnt < nLeftMsgCnt; nCnt++ )
            {
                packet = m_xPacketQueue.dequeue();
                if ( packet !is null )
                {
                    destroy(packet);
                }
            }
        }
        
        // ¸ðµçº¯¼ö¸¦ ÃÊ±âÈ­ ½ÃÄÑµÐ´Ù.
        InitActor();
    }

    // »ý¼º.
    public bool Create(ImageHandler handler, PFeature pstFeature, ubyte bMtn, ubyte bDir, ushort wPosX, ushort wPosY)
    {
        // Àü´ÞÀÎÀÚ Àû¿ë ¹× È®ÀÎ./////////////////////////////////////////////////////////////////////////////////
        if ( /*(pstFeature.Gender >= 0 && pstFeature.Gender < _MAX_GENDER) && */(bDir >= 0 && bDir < _MAX_DIRECTION) )
        {
            switch ( pstFeature.Gender )
            {
                case _GENDER_MAN:
                {
                    if ( (pstFeature.Dress < 0 && pstFeature.Dress >= _MAX_HERO_KIND) ||
                        (bMtn < 0 && bMtn >= _MAX_HERO_MTN) )  
                        return false;
                    else    
                    {
                        m_bActorImgIdx = _IMAGE_M_HUMAN;
                        m_dwFstFrame = SpriteInfo.m_stHeroSpr[bMtn].FirstFrame + pstFeature.Dress*_MAX_HERO_FRAME + bDir*10;
                        m_dwEndFrame = m_dwFstFrame + SpriteInfo.m_stHeroSpr[bMtn].FrameCount;
                        m_wDelay = SpriteInfo.m_stHeroSpr[bMtn].FrameDelay;
                        
                        //pstFeature.Hair = 2;
                    }

                    break;
                }

                case _GENDER_WOMAN:
                {
                    if ( (pstFeature.Dress < 0 && pstFeature.Dress >= _MAX_HERO_KIND) ||
                        (bMtn < 0 && bMtn >= _MAX_HERO_MTN) )  
                        return false;
                    else
                    {
                        m_bActorImgIdx = _IMAGE_WM_HUMAN;
                        m_dwFstFrame = SpriteInfo.m_stHeroSpr[bMtn].FirstFrame + pstFeature.Dress*_MAX_HERO_FRAME + bDir*10;
                        m_dwEndFrame = m_dwFstFrame + SpriteInfo.m_stHeroSpr[bMtn].FrameCount;
                        m_wDelay = SpriteInfo.m_stHeroSpr[bMtn].FrameDelay;

                        //pstFeature.Hair = 2;
                    }

                    break;
                }

                case _GENDER_MON:
                {   
                    if ( (pstFeature.Dress < 0 && pstFeature.Dress >= _MAX_MON_KIND) ||
                        (bMtn < 0 && bMtn >= _MAX_MON_MTN) )   
                        return false;
                    else
                    {
                        switch ( m_stFeature.Dress )
                        {
                            case 31:    // ½ÄÀÎÃÊ.
                            case 67:    // ÃË·æ½Å.
                            case 73:    // ºñ¸·¿øÃæ.
                            case 104:   // Àû¿ù¸¶.
                                bDir = 0;
                            case 106:   // Æø¾È°Å¹Ì.
                                bDir = 1;
                                break;
                            default:
                                break;
                        }
                        
                        SpriteInfo.SetMonFrameInfo(pstFeature.Dress);
                        m_bActorImgIdx = _IMAGE_MONSTER1 + (pstFeature.Dress / 10);
                        m_dwFstFrame = SpriteInfo.m_stMonSpr[bMtn].FirstFrame + (pstFeature.Dress%10)*_MAX_MON_FRAME + bDir*10;
                        m_dwEndFrame = m_dwFstFrame + SpriteInfo.m_stMonSpr[bMtn].FrameCount;
                        m_wDelay = SpriteInfo.m_stMonSpr[bMtn].FrameDelay;
                    }

                    break;
                }

                case _GENDER_NPC:
                {
                    if ( (pstFeature.Dress < 0 && pstFeature.Dress >= _MAX_NPC_KIND) ||
                        (bMtn < 0 && bMtn >= _MAX_NPC_MTN) )   
                        return false;
                    else
                    {
                        m_bActorImgIdx = _IMAGE_NPC;
                        bDir++;     // bDirÀº 0ÀÌ µÉ¼öµµ ÀÖ±â¶§¹®ÀÌ´Ù.
                        bDir = bDir/3;
                        m_dwFstFrame = SpriteInfo.m_stNPCSpr[bMtn].FirstFrame + pstFeature.Dress*_MAX_NPC_FRAME + bDir*10;
                        m_dwEndFrame = m_dwFstFrame + SpriteInfo.m_stNPCSpr[bMtn].FrameCount;
                        m_wDelay = SpriteInfo.m_stNPCSpr[bMtn].FrameDelay;
                    }

                    break;
                }
                
                default:
                    return false;
            }
            
            m_bCurrMtn      = bMtn;
            m_bCurrDir      = bDir;
            m_bMoveDir      = bDir;
            m_wPosX         = wPosX;
            m_wPosY         = wPosY;

            m_stFeature     = *pstFeature;
            
            m_pxActorImage  = handler[m_bActorImgIdx];
            m_dwCurrFrame   = m_dwFstFrame;
            m_bMoveDir      = m_bCurrDir;

            // PDS:
            /*D3DVECTOR       vNorm(0, 0, -1);
            m_avBoard[0] = D3DVERTEX(D3DVECTOR(-0.5f, 0.5f, 0), vNorm, 0, 0);
            m_avBoard[1] = D3DVERTEX(D3DVECTOR(-0.5f,-0.5f, 0), vNorm, 0, 1);
            m_avBoard[2] = D3DVERTEX(D3DVECTOR( 0.5f, 0.5f, 0), vNorm, 1, 0);
            m_avBoard[3] = D3DVERTEX(D3DVECTOR( 0.5f,-0.5f, 0), vNorm, 1, 1);*/
            
            return true;
        }
        
        return false;
    }
    
    public bool LoadEffect(ImageHandler handler, int effectNo, ubyte dir = 0)
    {
        ushort wFileType;
        PSpriteEffectInfo pstEffect = SpriteInfo.GetEffectInfo(effectNo);
        
        if ( pstEffect !is null )
        {
            m_bEffectImgIdx = cast(ubyte)pstEffect.wImgIdx;       
            
            m_dwFstEffectFrame = pstEffect.dwFstFrm;
            m_dwEndEffectFrame = pstEffect.dwEndFrm;
            
            if ( dir )
            {
                m_dwFstEffectFrame = m_dwFstEffectFrame + dir*10;
                m_dwEndEffectFrame = m_dwEndEffectFrame + dir*10;
            }
            m_dwCurrEffectFrame = m_dwFstEffectFrame;
            m_bEffectFrame = 0;
            
            m_bLightRadius[0] = pstEffect.bLightRadius[0];
            m_bLightRadius[1] = pstEffect.bLightRadius[1];
            
            m_bLightColor[0][0] = pstEffect.bLightColor[0][0];
            m_bLightColor[0][1] = pstEffect.bLightColor[0][1];
            m_bLightColor[0][2] = pstEffect.bLightColor[0][2];
            
            m_bLightColor[1][0] = pstEffect.bLightColor[1][0];
            m_bLightColor[1][1] = pstEffect.bLightColor[1][1];
            m_bLightColor[1][2] = pstEffect.bLightColor[1][2];
            
            for ( int nCnt = m_dwFstEffectFrame; nCnt < m_dwEndEffectFrame; nCnt++ )
            {
                if ( m_bEffectImgIdx == _IMAGE_MAGIC )
                {
                    wFileType = _TEXTR_FILE_MAGIC;
                }
                else if ( m_bEffectImgIdx == _IMAGE_MONMAGIC )
                {
                    wFileType = _TEXTR_FILE_MONMAGIC;
                }

                // TODO: check this
                //pxImgHandler.AddTextr(wFileType, m_bEffectImgIdx, nCnt);
            }
            return true;
            
        }
        return false;
    }

    public void DrawWithEffected(int nX, int nY, int nXSize, int nYSize, ALLEGRO_BITMAP* bmp, ushort wChooseColor1 = 0XFFFF, ushort wChooseColor2 = 0XFFFF, bool bFocused = false, ubyte bOpa = 50, ushort wState = _STATE_NOTUSED)
    {
        switch ( wState )
        {
            case _STATE_RED:
            case _STATE_GREEN:
            case _STATE_BLUE:       
            case _STATE_YELLOW:
            case _STATE_FUCHSIA:    
            case _STATE_GRAY:
                {
                    /*if ( m_wABlendDelay )
                        g_xMainWnd.DrawWithImageForCompClipRgnColor(nX, nY, nXSize, nYSize, pwSrc, _CLIP_WIDTH, _CLIP_HEIGHT, wState, bFocused, TRUE);
                    else
                        g_xMainWnd.DrawWithImageForCompClipRgnColor(nX, nY, nXSize, nYSize, pwSrc, _CLIP_WIDTH, _CLIP_HEIGHT, wState, bFocused, FALSE);
                    */
                    break;
                }
            case _STATE_ABLEND:
                {
                    if ( bFocused ) bOpa -= 20;
                    
                    if ( bOpa < 0 && bOpa > 100 )  bOpa = 0;
                    //g_xMainWnd.DrawWithABlendCompDataWithBackBuffer(nX, nY, nXSize, nYSize, pwSrc, _CLIP_WIDTH, _CLIP_HEIGHT, wChooseColor1, wChooseColor2, bOpa);
                break;
                }

            case _STATE_SHIELDUSE:
            case _STATE_NOTUSED:
            default:
                {
                    al_draw_bitmap(bmp, nX, nY, 0);
                    /*if ( !m_wABlendDelay )
                        g_xMainWnd.DrawWithImageForCompClipRgn(nX, nY, nXSize, nYSize, pwSrc, _CLIP_WIDTH, _CLIP_HEIGHT, wChooseColor1, wChooseColor2, bFocused);
                    else
                        g_xMainWnd.DrawWithABlendCompDataWithBackBuffer(nX, nY, nXSize, nYSize, pwSrc, _CLIP_WIDTH, _CLIP_HEIGHT, wChooseColor1, wChooseColor2, bOpa);*/
                    break;
                }
        }
    }

    //---------------------------------------------------------------------------------------------------------------//
    // ÇÁ·¹ÀÓ ¼³Á¤°ü·Ã.
    public bool CheckFeatureValidate(Feature stFeature)
    {
        //  if ( (stFeature.Gender >= 0 && stFeature.Gender < _MAX_GENDER) )
        {
            switch ( stFeature.Gender )
            {
                case _GENDER_MAN:
                case _GENDER_WOMAN:
                {
                    if ( stFeature.Dress < 0 && stFeature.Dress >= _MAX_HERO_KIND )
                        return false;
                    break;
                }
                case _GENDER_MON:
                {
                    if ( stFeature.Dress < 0 && stFeature.Dress >= _MAX_MON_KIND )
                        return false;
                    break;
                }

                case _GENDER_NPC:
                default:
                    return false;
            }
        }
        return true;
    }

    public bool ChangeFeature(Feature stFeature)
    {
        ImageHandler handler = PlayScene.m_xImage;
        
        if ( /*stFeature.Gender >= 0 && stFeature.Gender < _MAX_GENDER && */CheckFeatureValidate(stFeature) )
        {
            switch ( stFeature.Gender )
            {
                case _GENDER_MAN:
                {
                    if ( stFeature.Dress < 0 && stFeature.Dress >= _MAX_HERO_KIND )
                        return false;
                    else    
                    {
                        m_bActorImgIdx = _IMAGE_M_HUMAN;
                        m_dwFstFrame = SpriteInfo.m_stHeroSpr[m_bCurrMtn].FirstFrame + stFeature.Dress*_MAX_HERO_FRAME + m_bCurrDir*10;
                        m_dwEndFrame = m_dwFstFrame + SpriteInfo.m_stHeroSpr[m_bCurrMtn].FrameCount;
                        m_wDelay = SpriteInfo.m_stHeroSpr[m_bCurrMtn].FrameDelay;
                        
                        stFeature.Hair = 2;
                    }

                    break;
                }

                case _GENDER_WOMAN:
                {
                    if ( stFeature.Dress < 0 && stFeature.Dress >= _MAX_HERO_KIND )
                        return false;
                    else
                    {
                        m_bActorImgIdx = _IMAGE_WM_HUMAN;
                        m_dwFstFrame = SpriteInfo.m_stHeroSpr[m_bCurrMtn].FirstFrame + stFeature.Dress*_MAX_HERO_FRAME + m_bCurrDir*10;
                        m_dwEndFrame = m_dwFstFrame + SpriteInfo.m_stHeroSpr[m_bCurrMtn].FrameCount;
                        m_wDelay = SpriteInfo.m_stHeroSpr[m_bCurrMtn].FrameDelay;

                        stFeature.Hair = 2;
                    }

                    break;
                }

                case _GENDER_MON:
                {
                    if ( stFeature.Dress < 0 && stFeature.Dress >= _MAX_MON_KIND )
                        return false;
                    else
                    {
                        SpriteInfo.SetMonFrameInfo(stFeature.Dress);
                        m_bActorImgIdx = _IMAGE_MONSTER1 + (stFeature.Dress/10);
                        m_dwFstFrame = SpriteInfo.m_stMonSpr[m_bCurrMtn].FirstFrame + (stFeature.Dress%10)*_MAX_MON_FRAME + m_bCurrDir*10;
                        m_dwEndFrame = m_dwFstFrame + SpriteInfo.m_stMonSpr[m_bCurrMtn].FrameCount;
                        m_wDelay = SpriteInfo.m_stMonSpr[m_bCurrMtn].FrameDelay;
                    }

                    break;
                }

                // NPC´Â ÇÁ·¹ÀÓÀÌ ÀÏÁ¤ÇÏÁö ¾ÊÀ¸¹Ç·Î µû·Î Àû¿ë½ÃÅ²´Ù.
                case _GENDER_NPC:
                default:
                    return false;
            }
            m_stFeature     = stFeature;
            m_pxActorImage  = handler[m_bActorImgIdx];
            m_dwCurrFrame   = m_dwFstFrame;
            
            return true;
        }
        
        return false;
    }

    public bool SetMotionFrame(ubyte bMtn, ubyte bDir)
    {
        if ( /*(bMtn < 0 && bMtn >= _MAX_HERO_MTN) || */(bDir < 0 && bDir >= _MAX_DIRECTION) )
            return false;
        
        switch ( m_stFeature.Gender )
        {
            case _GENDER_MAN:
            {
                m_dwFstFrame = SpriteInfo.m_stHeroSpr[bMtn].FirstFrame + m_stFeature.Dress*_MAX_HERO_FRAME + bDir*10;
                m_dwEndFrame = m_dwFstFrame + SpriteInfo.m_stHeroSpr[bMtn].FrameCount;
                m_wDelay = SpriteInfo.m_stHeroSpr[bMtn].FrameDelay;

                break;
            }
            
            case _GENDER_WOMAN:
            {
                m_dwFstFrame = SpriteInfo.m_stHeroSpr[bMtn].FirstFrame + m_stFeature.Dress*_MAX_HERO_FRAME + bDir*10;
                m_dwEndFrame = m_dwFstFrame + SpriteInfo.m_stHeroSpr[bMtn].FrameCount;
                m_wDelay = SpriteInfo.m_stHeroSpr[bMtn].FrameDelay;

                break;
            }
            
            case _GENDER_MON:
            {
                if ( m_stFeature.Dress == 31 && bMtn == _MT_MON_STAND )
                    bDir = 0;
                
                switch ( m_stFeature.Dress )
                {
                    case 31:    // ½ÄÀÎÃÊ.
                    case 67:    // ÃË·æ½Å.
                    case 73:    // ºñ¸·¿øÃæ.
                    case 104:   // Àû¿ù¸¶.
                        bDir = 0;
                    case 106:   // Æø¾È°Å¹Ì.
                        bDir = 1;
                        break;
                    default:
                        break;
                }
                
                SpriteInfo.SetMonFrameInfo(m_stFeature.Dress);
                m_dwFstFrame = SpriteInfo.m_stMonSpr[bMtn].FirstFrame + (m_stFeature.Dress%10)*_MAX_MON_FRAME + bDir*10;
                m_dwEndFrame = m_dwFstFrame + SpriteInfo.m_stMonSpr[bMtn].FrameCount;
                m_wDelay = SpriteInfo.m_stMonSpr[bMtn].FrameDelay;

                break;
            }

            case _GENDER_NPC:
            {
                bDir++;     // bDirÀº 0ÀÌ µÉ¼öµµ ÀÖ±â¶§¹®ÀÌ´Ù.
                bDir = bDir/3;
                m_dwFstFrame = SpriteInfo.m_stNPCSpr[bMtn].FirstFrame + m_stFeature.Dress*_MAX_NPC_FRAME + bDir*10;
                m_dwEndFrame = m_dwFstFrame + SpriteInfo.m_stNPCSpr[bMtn].FrameCount;
                m_wDelay = SpriteInfo.m_stNPCSpr[bMtn].FrameDelay;

                break;
            }

            default:
                return false;
        }
        
        m_bCurrMtn      = bMtn;
        m_bCurrDir      = bDir;
        m_bMoveDir      = bDir;
        m_dwCurrFrame   = m_dwFstFrame;
        m_wCurrDelay    = 0;
        
        if ( m_bCurrDir == _DIRECTION_LIST_8 )
            m_bMoveNextFrmCnt = 2;
        else
            m_bMoveNextFrmCnt = 5;
        
        
        return true;
    }

    // Ä³¸¯ÅÍ ÀÌµ¿.
    public void SetMoving()
    {
        uint wFrmCnt  = m_dwEndFrame  - m_dwFstFrame;
        uint wCurrFrm = m_dwCurrFrame - m_dwFstFrame;
        
        switch ( m_bMoveDir )
        {
            case _DIRECTION_LIST_1:
                m_shShiftPixelX =  0;
                
                if ( wCurrFrm < (6-m_bMoveNextFrmCnt) )
                    m_shShiftPixelY = -(_CELL_HEIGHT/wFrmCnt * (wCurrFrm+1))*m_bMoveSpeed;
                else
                    m_shShiftPixelY =  (_CELL_HEIGHT/wFrmCnt * (wFrmCnt - wCurrFrm -1))*m_bMoveSpeed;
                break;
            case _DIRECTION_LIST_2:
                if ( wCurrFrm < (6-m_bMoveNextFrmCnt) )
                    m_shShiftPixelX =  (_CELL_WIDTH /wFrmCnt * (wCurrFrm+1))*m_bMoveSpeed;
                else
                    m_shShiftPixelX = -(_CELL_WIDTH /wFrmCnt * (wFrmCnt - wCurrFrm -1))*m_bMoveSpeed;
                
                if ( wCurrFrm < (6-m_bMoveNextFrmCnt) )
                    m_shShiftPixelY = -(_CELL_HEIGHT/wFrmCnt * (wCurrFrm+1))*m_bMoveSpeed;
                else
                    m_shShiftPixelY =  (_CELL_HEIGHT/wFrmCnt * (wFrmCnt - wCurrFrm -1))*m_bMoveSpeed;
                break;
            case _DIRECTION_LIST_3:
                if ( wCurrFrm < (6-m_bMoveNextFrmCnt) )
                    m_shShiftPixelX =  (_CELL_WIDTH /wFrmCnt * (wCurrFrm+1))*m_bMoveSpeed;
                else
                    m_shShiftPixelX = -(_CELL_WIDTH /wFrmCnt * (wFrmCnt - wCurrFrm -1))*m_bMoveSpeed;
                
                m_shShiftPixelY = 0;
                break;
            case _DIRECTION_LIST_4:
                if ( wCurrFrm < (6-m_bMoveNextFrmCnt) )
                    m_shShiftPixelX =  (_CELL_WIDTH /wFrmCnt * (wCurrFrm+1))*m_bMoveSpeed;
                else
                    m_shShiftPixelX = -(_CELL_WIDTH /wFrmCnt * (wFrmCnt - wCurrFrm -1))*m_bMoveSpeed;
                
                if ( wCurrFrm < (6-m_bMoveNextFrmCnt) )
                    m_shShiftPixelY =  (_CELL_HEIGHT/wFrmCnt * (wCurrFrm+1))*m_bMoveSpeed;
                else
                    m_shShiftPixelY = -(_CELL_HEIGHT/wFrmCnt * (wFrmCnt - wCurrFrm -1))*m_bMoveSpeed;
                break;
            case _DIRECTION_LIST_5:
                m_shShiftPixelX =  0;
                
                if ( wCurrFrm < (6-m_bMoveNextFrmCnt) )
                    m_shShiftPixelY =  (_CELL_HEIGHT/wFrmCnt * (wCurrFrm+1))*m_bMoveSpeed;
                else
                    m_shShiftPixelY = -(_CELL_HEIGHT/wFrmCnt * (wFrmCnt - wCurrFrm -1))*m_bMoveSpeed;
                break;
            case _DIRECTION_LIST_6:
                if ( wCurrFrm < (6-m_bMoveNextFrmCnt) )
                    m_shShiftPixelX = -(_CELL_WIDTH /wFrmCnt * (wCurrFrm+1))*m_bMoveSpeed;
                else
                    m_shShiftPixelX =  (_CELL_WIDTH /wFrmCnt * (wFrmCnt - wCurrFrm -1))*m_bMoveSpeed;
                
                if ( wCurrFrm < (6-m_bMoveNextFrmCnt) )
                    m_shShiftPixelY =  (_CELL_HEIGHT/wFrmCnt * (wCurrFrm+1))*m_bMoveSpeed;
                else
                    m_shShiftPixelY = -(_CELL_HEIGHT/wFrmCnt * (wFrmCnt - wCurrFrm -1))*m_bMoveSpeed;
                break;
            case _DIRECTION_LIST_7:
                if ( wCurrFrm < (6-m_bMoveNextFrmCnt) )
                    m_shShiftPixelX = -(_CELL_WIDTH /wFrmCnt * (wCurrFrm+1))*m_bMoveSpeed;
                else
                    m_shShiftPixelX =  (_CELL_WIDTH /wFrmCnt * (wFrmCnt - wCurrFrm -1))*m_bMoveSpeed;
                
                m_shShiftPixelY =  0;
                break;
            case _DIRECTION_LIST_8:
                if ( wCurrFrm < (6-m_bMoveNextFrmCnt) )
                    m_shShiftPixelX = -(_CELL_WIDTH /wFrmCnt * (wCurrFrm+1))*m_bMoveSpeed;
                else
                    m_shShiftPixelX =  (_CELL_WIDTH /wFrmCnt * (wFrmCnt - wCurrFrm -1))*m_bMoveSpeed;
                
                if ( wCurrFrm < (6-m_bMoveNextFrmCnt) )
                    m_shShiftPixelY = -(_CELL_HEIGHT/wFrmCnt * (wCurrFrm+1))*m_bMoveSpeed;
                else
                    m_shShiftPixelY =  (_CELL_HEIGHT/wFrmCnt * (wFrmCnt - wCurrFrm -1))*m_bMoveSpeed;
                break;
            default: break;
        }
    }

    public void SetBackStepMoving()
    {
        switch ( m_bMoveDir )
        {
            case _DIRECTION_LIST_1:
                m_shShiftPixelX =  0;
                
                if ( m_bBackStepFrame < (6-m_bMoveNextFrmCnt) )
                    m_shShiftPixelY = -(_CELL_HEIGHT/m_bBackStepFrameCnt * (m_bBackStepFrame+1))*m_bMoveSpeed;
                else
                    m_shShiftPixelY =  (_CELL_HEIGHT/m_bBackStepFrameCnt * (m_bBackStepFrameCnt - m_bBackStepFrame -1))*m_bMoveSpeed;
                break;
            case _DIRECTION_LIST_2:
                if ( m_bBackStepFrame < (6-m_bMoveNextFrmCnt) )
                    m_shShiftPixelX =  (_CELL_WIDTH /m_bBackStepFrameCnt * (m_bBackStepFrame+1))*m_bMoveSpeed;
                else
                    m_shShiftPixelX = -(_CELL_WIDTH /m_bBackStepFrameCnt * (m_bBackStepFrameCnt - m_bBackStepFrame -1))*m_bMoveSpeed;
                
                if ( m_bBackStepFrame < (6-m_bMoveNextFrmCnt) )
                    m_shShiftPixelY = -(_CELL_HEIGHT/m_bBackStepFrameCnt * (m_bBackStepFrame+1))*m_bMoveSpeed;
                else
                    m_shShiftPixelY =  (_CELL_HEIGHT/m_bBackStepFrameCnt * (m_bBackStepFrameCnt - m_bBackStepFrame -1))*m_bMoveSpeed;
                break;
            case _DIRECTION_LIST_3:
                if ( m_bBackStepFrame < (6-m_bMoveNextFrmCnt) )
                    m_shShiftPixelX =  (_CELL_WIDTH /m_bBackStepFrameCnt * (m_bBackStepFrame+1))*m_bMoveSpeed;
                else
                    m_shShiftPixelX = -(_CELL_WIDTH /m_bBackStepFrameCnt * (m_bBackStepFrameCnt - m_bBackStepFrame -1))*m_bMoveSpeed;
                
                m_shShiftPixelY = 0;
                break;
            case _DIRECTION_LIST_4:
                if ( m_bBackStepFrame < (6-m_bMoveNextFrmCnt) )
                    m_shShiftPixelX =  (_CELL_WIDTH /m_bBackStepFrameCnt * (m_bBackStepFrame+1))*m_bMoveSpeed;
                else
                    m_shShiftPixelX = -(_CELL_WIDTH /m_bBackStepFrameCnt * (m_bBackStepFrameCnt - m_bBackStepFrame -1))*m_bMoveSpeed;
                
                if ( m_bBackStepFrame < (6-m_bMoveNextFrmCnt) )
                    m_shShiftPixelY =  (_CELL_HEIGHT/m_bBackStepFrameCnt * (m_bBackStepFrame+1))*m_bMoveSpeed;
                else
                    m_shShiftPixelY = -(_CELL_HEIGHT/m_bBackStepFrameCnt * (m_bBackStepFrameCnt - m_bBackStepFrame -1))*m_bMoveSpeed;
                break;
            case _DIRECTION_LIST_5:
                m_shShiftPixelX =  0;
                
                if ( m_bBackStepFrame < (6-m_bMoveNextFrmCnt) )
                    m_shShiftPixelY =  (_CELL_HEIGHT/m_bBackStepFrameCnt * (m_bBackStepFrame+1))*m_bMoveSpeed;
                else
                    m_shShiftPixelY = -(_CELL_HEIGHT/m_bBackStepFrameCnt * (m_bBackStepFrameCnt - m_bBackStepFrame -1))*m_bMoveSpeed;
                break;
            case _DIRECTION_LIST_6:
                if ( m_bBackStepFrame < (6-m_bMoveNextFrmCnt) )
                    m_shShiftPixelX = -(_CELL_WIDTH /m_bBackStepFrameCnt * (m_bBackStepFrame+1))*m_bMoveSpeed;
                else
                    m_shShiftPixelX =  (_CELL_WIDTH /m_bBackStepFrameCnt * (m_bBackStepFrameCnt - m_bBackStepFrame -1))*m_bMoveSpeed;
                
                if ( m_bBackStepFrame < (6-m_bMoveNextFrmCnt) )
                    m_shShiftPixelY =  (_CELL_HEIGHT/m_bBackStepFrameCnt * (m_bBackStepFrame+1))*m_bMoveSpeed;
                else
                    m_shShiftPixelY = -(_CELL_HEIGHT/m_bBackStepFrameCnt * (m_bBackStepFrameCnt - m_bBackStepFrame -1))*m_bMoveSpeed;
                break;
            case _DIRECTION_LIST_7:
                if ( m_bBackStepFrame < (6-m_bMoveNextFrmCnt) )
                    m_shShiftPixelX = -(_CELL_WIDTH /m_bBackStepFrameCnt * (m_bBackStepFrame+1))*m_bMoveSpeed;
                else
                    m_shShiftPixelX =  (_CELL_WIDTH /m_bBackStepFrameCnt * (m_bBackStepFrameCnt - m_bBackStepFrame -1))*m_bMoveSpeed;
                
                m_shShiftPixelY =  0;
                break;
            case _DIRECTION_LIST_8:
                if ( m_bBackStepFrame < (6-m_bMoveNextFrmCnt) )
                    m_shShiftPixelX = -(_CELL_WIDTH /m_bBackStepFrameCnt * (m_bBackStepFrame+1))*m_bMoveSpeed;
                else
                    m_shShiftPixelX =  (_CELL_WIDTH /m_bBackStepFrameCnt * (m_bBackStepFrameCnt - m_bBackStepFrame -1))*m_bMoveSpeed;
                
                if ( m_bBackStepFrame < (6-m_bMoveNextFrmCnt) )
                    m_shShiftPixelY = -(_CELL_HEIGHT/m_bBackStepFrameCnt * (m_bBackStepFrame+1))*m_bMoveSpeed;
                else
                    m_shShiftPixelY =  (_CELL_HEIGHT/m_bBackStepFrameCnt * (m_bBackStepFrameCnt - m_bBackStepFrame -1))*m_bMoveSpeed;
                break;  
            default: break;
        }
    }

    public void SetMoved()
    {
        switch ( m_bMoveDir )
        {
            case _DIRECTION_LIST_1:
                m_shShiftTileX =  0;
                m_shShiftTileY = -m_bMoveSpeed;
                break;
            case _DIRECTION_LIST_2:
                m_shShiftTileX =  m_bMoveSpeed;
                m_shShiftTileY = -m_bMoveSpeed;
                break;
            case _DIRECTION_LIST_3:
                m_shShiftTileX =  m_bMoveSpeed;
                m_shShiftTileY =  0;
                break;
            case _DIRECTION_LIST_4:
                m_shShiftTileX =  m_bMoveSpeed;
                m_shShiftTileY =  m_bMoveSpeed;
                break;
            case _DIRECTION_LIST_5:
                m_shShiftTileX =  0;
                m_shShiftTileY =  m_bMoveSpeed;
                break;
            case _DIRECTION_LIST_6:
                m_shShiftTileX = -m_bMoveSpeed;
                m_shShiftTileY =  m_bMoveSpeed;
                break;
            case _DIRECTION_LIST_7:
                m_shShiftTileX = -m_bMoveSpeed;
                m_shShiftTileY =  0;
                break;
            case _DIRECTION_LIST_8:
                m_shShiftTileX = -m_bMoveSpeed;
                m_shShiftTileY = -m_bMoveSpeed;
                break;
            default:
                m_shShiftTileX = 0;
                m_shShiftTileY = 0;
                break;
        }
        m_wPosX += m_shShiftTileX;
        m_wPosY += m_shShiftTileY;
        m_shShiftPixelX = 0;
        m_shShiftPixelY = 0;
    }

    public ushort GetCharState()
    {
        ushort wCharState = 0XFFFF;
        
        //  if( m_nState &   0X100000 )         wCharState = _STATE_SHIELDUSE;
        if( m_nState &   0X800000 )         wCharState = _STATE_ABLEND;
        if( m_nState &  0X4000000 )         wCharState = _STATE_GRAY;
        if( m_nState &  0X8000000 )         wCharState = _STATE_FUCHSIA;
        if( m_nState & 0X10000000 )         wCharState = _STATE_YELLOW;
        if( m_nState & 0X20000000 )         wCharState = _STATE_BLUE;
        if( m_nState & 0X40000000 )         wCharState = _STATE_RED;
        if( m_nState & 0X80000000 )         wCharState = _STATE_GREEN;
        
        m_wStateClr = _STATE_NOTUSED;
        
        if ( m_wStateClr == _STATE_ABLEND || m_wStateClr == _STATE_SHIELDUSE )
            m_wStateClr = _STATE_NOTUSED;
        
        return wCharState;
    }

    //---------------------------------------------------------------------------------------------------------------//
    
    public void ChatMsgAdd()
    {
        // TODO: formattedRead throws exception on wrong number of args.
        // I have left C++ version here for reference.
        const string fmt = "%s` %s` %s` %s` %s";
        string szDivied;

        MyPen.divideString(50, m_nDividedChatLine, m_szChatMsg, szDivied);

        uint count;
        try
        {
            count = formattedRead(szDivied, fmt, 
                &m_szChatMsgArg[0], &m_szChatMsgArg[1], &m_szChatMsgArg[2], &m_szChatMsgArg[3], &m_szChatMsgArg[4]);
        }
        catch
        {
        }
        //sscanf(szDivied, "%[^`]%*c %[^`]%*c %[^`]%*c %[^`]%*c %[^`]%*c", 
        //       m_szChatMsgArg[0], m_szChatMsgArg[1], m_szChatMsgArg[2], m_szChatMsgArg[3], m_szChatMsgArg[4]);

        m_wCurrChatDelay = 0;
    }

    public void ShowMessage(int nLoopTime)
    {
        const ALLEGRO_COLOR msgback_color = al_map_rgba(80, 60, 40, 120);

        if ( m_szChatMsg.length > 0 )
        {
            m_wCurrChatDelay += nLoopTime;
        }
        
        if ( m_wCurrChatDelay > 3000 )
        {
            m_wCurrChatDelay = 0;
            m_szChatMsg = m_szChatMsg.init;
            m_szChatMsgArg = m_szChatMsgArg.init;
        }
        
        if ( m_szChatMsg.length > 0 )
        {
            int nStartX = m_shScrnPosX + 28 - _CHAT_WIDTH/2;
            int nStartY;
            
/*      if ( m_stFeatureEx.Horse == _HORSE_NONE )
            nStartY = m_shScrnPosY - 55;
        else
*/          nStartY = m_shScrnPosY - 55;
            
            
            IntRectangle rcBack;
            if ( m_nDividedChatLine == 1 )
            {
                IntPoint size = MyPen.measureText(m_szChatMsgArg[0]);
                nStartX = m_shScrnPosX + 28 - (size.getX() / 2);
                rcBack.set(nStartX, nStartY - 14 - 4, nStartX + size.getX(), nStartY);
            }
            else
                rcBack.set(nStartX, nStartY-m_nDividedChatLine*14-4, nStartX+_CHAT_WIDTH, nStartY);

            al_draw_filled_rectangle(nStartX, nStartY, 
                                     nStartX + rcBack.getWidth(), nStartY + rcBack.getHeight(), msgback_color);
            
            for ( int i = 0; i < m_nDividedChatLine; i++ )
            {
                immutable string chatstr = m_szChatMsgArg[i];
                IntPoint size = MyPen.measureText(chatstr);
                
                if ( m_nDividedChatLine != 1 )
                {
                    if ( i == m_nDividedChatLine-1 )
                    {
                        nStartX += (_CHAT_WIDTH - size.getX())/2;
                    }
                }

                MyPen.drawText(nStartX, nStartY-(m_nDividedChatLine-i)*14,
                                AlWhite, chatstr);
            }
        }
    }

    public void StruckMsgReassign()
    {
        int nCnt, nLeftMsgCnt;
        PPacketMsg packet;
        
        packet  = null;
        nLeftMsgCnt = m_xPacketQueue.count();
        
        if ( nLeftMsgCnt > 1 )
        {
            for ( nCnt = 0; nCnt < nLeftMsgCnt; nCnt++ )
            {
                packet = m_xPacketQueue.dequeue();
                if ( packet !is null )
                {
                    if ( packet.stDefMsg.Ident == SM_STRUCK )
                    {
                        destroy(packet);
                    }
                    else
                    {
                        m_xPacketQueue.enqueue(packet);
                    }
                }
            }
        }
    }
    
    public void PlayActSound()
    {
        int nWaveNum = -1;
        
        const int nActorSndTbl[100] = 
        [
            -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   //  0 -  9
            -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,   // 10 - 19
            160, 161, 100, 101, 102, 163,   0, 162,  83,  80,   // 20 - 29
            1,  10,  20,  21,  22,  23, 150,  24,  25,  26,   // 30 - 39
            27,  30,  32,  31, 151,  34,  28,  18,  40,  50,   // 40 - 49
            51,  52,  53, 152,  36,  72,  37,  38,  43,  44,   // 50 - 59
            45,  48,  49,  90,  91,  70,  73, 140,  74, 120,   // 60 - 69
            121,  81,  82,  41,  42,  39, 110, 111, 112, 130,   // 70 - 79
            164,  46,  47,  61,  62,  63,  71,  -1,  -1, 170,   // 80 - 89
            171,  33,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1   // 90 - 99
        ];
        
        if ( m_dwCurrFrame == m_dwFstFrame+1 && m_bCurrMtn == _MT_MON_APPEAR )          // ³ªÅ¸³ª±â.
        {
            nWaveNum = 200 + nActorSndTbl[m_stFeature.Dress]*10;
        }
        else if ( (m_dwCurrFrame == m_dwFstFrame+1) && (m_bCurrMtn == _MT_MON_STAND || _MT_MON_WALK) )      // ¸ØÃçÀÖ±â.
        {
            uint nRand = random();
            
            if ( m_bFstSoundPlayed && !m_bIsDead )
            {
                if ( !(nRand%25) )
                    nWaveNum = 200 + nActorSndTbl[m_stFeature.Dress]*10 + 1;
                else
                    nWaveNum = -1;
            }
            else
            {
                if ( !(nRand%3) && !m_bIsDead )
                {
                    nWaveNum = 200 + nActorSndTbl[m_stFeature.Dress]*10 + 1;
                    m_bFstSoundPlayed = true;
                }
                else
                    nWaveNum = -1;
            }
        }
        else if ( m_dwCurrFrame == m_dwFstFrame+1 && m_bCurrMtn == _MT_MON_ATTACK_A )   // ÀÏ¹Ý°ø°Ý1.
        {
            nWaveNum = 200 + nActorSndTbl[m_stFeature.Dress]*10 + 2;
        }
        else if ( m_dwCurrFrame == m_dwFstFrame+2 && m_bCurrMtn == _MT_MON_ATTACK_A )   // ÀÏ¹Ý°ø°Ý1.
        {
            nWaveNum = 200 + nActorSndTbl[m_stFeature.Dress]*10 + 3;
        }
        else if ( m_dwCurrFrame == m_dwFstFrame+1 && m_bCurrMtn == _MT_MON_HITTED )
        {
            if ( m_stFeature.Gender < 2 )
            {
                switch ( m_stHitter.Weapon )
                {
                    case 21: case 24: // ´Ü°Ë, ºñ´Ü°Ë.
                    case 8: case 9: // ¸ñ°Ë, ¾Æ¸®¼ö¸ñ°Ë.
                    case 18:  case 22:  case 23:  case 26:  case 27:  case 28:  case 30:  // »ç¸ð°Ë. Ã»µ¿°Ë. Ã¶°Ë. Ã»À½°Ë. º®»ç°Ë. Ãµ·É. °î¼º°Ë.
                    case 1:   case 4:  case 11:  case 13:  case 14:  case 20:  case 25:  case 29:  case 31:   // À¯¿ùµµ. ¹¬Ã»´ëµµ. À°ÇÕµµ. ±ºµµ. µµ·æº¸µµ. »ç°¢µµ. ¼¼Ã·µµ. ¿¹µµ. ÃÊÈ¥µµ.
                        nWaveNum = 70;
                        break;
                    case 5:  case 10:  case 12: // »ïÀû´ëºÎ. Ã»µ¿µµ³¢. ¿¬ÀÚºÎ.
                    case 15:  // ÆÄ³úÁø´ç.
                        nWaveNum = 71;
                        break;
                    case 2:  case 3:  case 6:  case 7: case 16:  case 17: case 19:  // »ïÁöÃ¢. ÃµÇü¸ñ. È«¾ÆÃ¢. °î±ªÀÌ. Ã»¸¶Ã¢, ¿ë¾ÆÀå. Á¦¸¶ºÀ
                        nWaveNum = 72;
                        break;
                    default:
                        nWaveNum = 73;  // ¸Ç¼Õ.
                        break;
                }
            }
        }
        else if ( m_dwCurrFrame == m_dwFstFrame+2 && m_bCurrMtn == _MT_MON_HITTED )     // ¸Â±â.
        {
            nWaveNum = 200 + nActorSndTbl[m_stFeature.Dress]*10 + 4;
        }
        else if ( m_dwCurrFrame == m_dwFstFrame+2 && m_bCurrMtn == _MT_MON_DIE )        // Á×±â1.
        {
            nWaveNum = 200 + nActorSndTbl[m_stFeature.Dress]*10 + 5;
        }
        else if ( m_dwCurrFrame == m_dwFstFrame+3 && m_bCurrMtn == _MT_MON_DIE )        // Á×±â2.
        {
            nWaveNum = 200 + nActorSndTbl[m_stFeature.Dress]*10 + 6;
        }

        // TODO:
        /*  if ( nWaveNum != -1 )
        g_xSound.PlayActorWav(m_wPosX, m_wPosY, g_xGameProc.m_xMyHero.m_wPosX, g_xGameProc.m_xMyHero.m_wPosY, nWaveNum);
*/
    }

    // µ¿ÀÛ ÁøÇà.
    public bool UpdatePacketState()
    {
        PPacketMsg packet = null;
        
        if ( m_bCurrMtn == _MT_MON_STAND )
        {
            int nLeftMsgCnt = m_xPacketQueue.count();
            
            if ( nLeftMsgCnt > 0 )
            {
                packet = m_xPacketQueue.dequeue();
                
                if ( nLeftMsgCnt >= 3 ) m_bMsgHurryCheck = true;
                else m_bMsgHurryCheck = false;
                
                if ( packet !is null )
                {
                    switch ( packet.stDefMsg.Ident )
                    {
                        case SM_NOWDEATH:
                        case SM_DEATH:              {   OnDeath(packet);               break;  }
                        case SM_WALK:               {   OnWalk(packet);                break;  }
                        case SM_TURN:               {   OnTurn(packet);                break;  }
                        case SM_DIGUP:              {   OnDigup(packet);               break;  }
                        case SM_DIGDOWN:            {   OnDigDown(packet);             break;  }
                        case SM_FEATURECHANGED:     {   OnFeatureChanged(packet);      break;  }
                        case SM_OPENHEALTH:         {   OnOpenHealth(packet);          break;  }
                        case SM_CLOSEHEALTH:        {   OnCloseHealth(packet);         break;  }
                        case SM_CHANGELIGHT:        {   OnChangeLight(packet);         break;  }
                        case SM_CHANGENAMECOLOR:    {   OnChangeNameClr(packet);       break;  }
                        case SM_USERNAME:           {   OnUserName(packet);            break;  }
                        case SM_HEALTHSPELLCHANGED: {   OnHealthSpellChanged(packet);  break;  }
                        case SM_BACKSTEP:           {   OnBackStep(packet);            break;  }
                        case SM_STRUCK:             {   OnStruck(packet);              break;  }
                        case SM_HIT:                {   OnHit(packet);                 break;  }
                        case SM_FLYAXE:             {   OnFlyAxe(packet);              break;  }
                        case SM_LIGHTING:           {   OnLighting(packet);            break;  }
                        case SM_SKELETON:           {   OnWalk(packet);                break;  }

                        default: break;  
                    }
                    destroy(packet);
                }

                return true;
            }
        }
        return false;
    }

    public void UpdateMotionState(int nLoopTime, bool bIsMoveTime)
    {
        m_wABlendCurrDelay += nLoopTime;
        
        if ( m_wABlendCurrDelay >= m_wABlendDelay )
        {
            m_wABlendCurrDelay = 0;
            m_wABlendDelay     = 0;
            m_bABlendRev       = false;
        }
        
        if ( m_bCurrMtn == _MT_MON_DIE && m_dwCurrFrame >= m_dwEndFrame-1 )
        {
            m_bIsDead = true;
        }
        
        if ( m_bIsDead )
        {
            SetMotionFrame(_MT_MON_DIE, m_bCurrDir);
            m_dwCurrFrame = m_dwEndFrame - 1;
            return;
        }
        
        if ( UpdateMove(bIsMoveTime) )
        {
            UpdatePacketState();
            return;
        }
        else
        {
            m_wCurrDelay       += nLoopTime;
            
            if ( m_wCurrDelay > m_wDelay )
            {
                m_wCurrDelay = 0;
                
                if ( m_dwCurrFrame < m_dwEndFrame )
                {
                    m_dwCurrFrame++;
                    
                    PlayActSound();
                    
                    if ( m_bMsgHurryCheck )         
                    {
                        m_wDelay = m_wDelay/2;
                        m_bMsgHurryCheck = false;
                    }
                }
            }
            
            UpdatePacketState();
            
            if ( m_dwCurrFrame >= m_dwEndFrame )
            {
                m_shShiftTileX = 0;
                m_shShiftTileY = 0;
                m_shShiftPixelX = 0;
                m_shShiftPixelY = 0;
                
                m_bReverse = false;
                m_dwCurrEffectFrame = 0;
                m_dwFstEffectFrame = 0;
                m_dwEndEffectFrame = 0;
                m_bEffectFrame = 0;
                m_bEffectFrameCnt = _DEFAULT_SPELLFRAME;
                m_bUseEffect = false;
                m_bUseSwordEffect = false;
                m_dwCurrFrame = m_dwFstFrame;
                
                SetMotionFrame(_MT_MON_STAND, m_bCurrDir);
                
                if ( m_nState & 0X1 )        // ¼®È­»óÅÂ.
                {
                    switch ( m_stFeature.Dress )
                    {
                        case 83: //ÁÖ¸¶½ÅÀå.
                        case 84: //ÁÖ¸¶È£¹ý.
                        case 85: //ÁÖ¸¶¿Õ.
                            SetMotionFrame(_MT_MON_APPEAR, 0);
                            m_dwEndFrame = m_dwFstFrame + 1;
                            m_bCurrMtn = _MT_MON_STAND;
                            break;
                        default: break;
                    }
                }
                
                if ( m_bAppearState == _DIG_DOWN )
                    m_bAppearState = _DIG_DOWNDEL;
            }
        }
    }

    public bool UpdateMove(bool bIsMoveTime)
    {
        if ( m_bCurrMtn == _MT_MON_WALK || m_bCurrMtn == _MT_PUSHBACK )
        {
            m_wCurrDelay = 0;
            
            if ( bIsMoveTime )
            {
                if ( m_bBackStepFrameCnt )
                {
                    m_dwCurrFrame += 2;
                }
                else
                {
                    m_dwCurrFrame++;
                    
                    if ( m_bMsgHurryCheck )         
                    {
                        m_dwCurrFrame++;
                    }
                }
                
                if ( m_dwCurrFrame >= m_dwEndFrame-m_bMoveNextFrmCnt && !m_bIsMoved )
                {
                    SetMoved();
                    m_bIsMoved = true;
                }
            }
            
            // ¿¬¼ÓÀûÀÎ ÇÁ·¹ÀÓ Áß¿¡¼­ ÇØ¾ßÇÒÀÏ.
            if ( m_dwCurrFrame >= m_dwEndFrame )
            {
                m_dwCurrFrame = m_dwEndFrame - 1;
                
                m_shShiftTileX = 0;
                m_shShiftTileY = 0;
                m_shShiftPixelX = 0;
                m_shShiftPixelY = 0;
                
                m_dwCurrEffectFrame = 0;
                m_dwFstEffectFrame = 0;
                m_dwEndEffectFrame = 0;
                m_bEffectFrame = 0;
                m_bUseEffect = false;
                m_bUseSwordEffect = false;
                m_dwCurrFrame = m_dwFstFrame;
                
                m_bBackStepFrame = 0;
                m_bBackStepFrameCnt = 0;
                
                m_bIsMoved = false;
                
                SetMotionFrame(_MT_MON_STAND, m_bCurrDir);
            }
            else if ( m_dwCurrFrame < m_dwEndFrame )
            {
                if ( m_bCurrMtn != _MT_PUSHBACK )
                    SetMoving();
                else
                    SetBackStepMoving();
            }
            return true;
        }
        return false;
    }
    
    // À¯Çüº° ÆÐÅ¶»óÅÂÀû¿ë.
    public void OnCharDescPacket(PPacketMsg packet)
    {
        CharDesc    stCharDesc;
        Feature     stFeature;

        DecodeBuffer(packet.szEncodeData, &stCharDesc, stCharDesc.sizeof);
        
        memcpy(&stFeature, &stCharDesc.Feature, stFeature.sizeof);
        m_nState = stCharDesc.Status;
        
        if ( m_nState & 0X2 ) m_bOpenHealth = true;
        else m_bOpenHealth = false;
        
        ChangeFeature(stFeature);
    }

    public void OnUserName(PPacketMsg packet)
    {
        m_szName = DecodeString(packet.szEncodeData.idup);
        m_dwNameClr = GetUserNameColor(packet.stDefMsg.Param);
    }

    public void OnChangeNameClr(PPacketMsg packet)
    {
        m_dwNameClr = GetUserNameColor(packet.stDefMsg.Param);
    }

    public void OnChangeLight(PPacketMsg packet)
    {
        m_bLightSize = cast(ubyte)packet.stDefMsg.Param;
    }

    public void OnOpenHealth(PPacketMsg packet)
    {
        m_bOpenHealth = true;
    }

    public void OnCloseHealth(PPacketMsg packet)
    {
        m_bOpenHealth = false;
    }

    public void OnFeatureChanged(PPacketMsg packet)
    {
        Feature stFeature;
        int nFeature = MakeLong(packet.stDefMsg.Param, packet.stDefMsg.Tag);

        memcpy(&stFeature, &nFeature, nFeature.sizeof);
        
        ChangeFeature(stFeature);
    }

    public void OnHealthSpellChanged(PPacketMsg packet)
    {
        m_wHP    = packet.stDefMsg.Param;
        m_wMP    = packet.stDefMsg.Tag;
        m_wMAXHP = packet.stDefMsg.Series;
        m_bHPPercent = cast(ubyte)get_percentage(m_wHP, m_wMAXHP);
    }

    public void OnWalk(PPacketMsg packet)
    {
        ubyte bDir;
        //m_wPosX = packet.stDefMsg.Param;
        //m_wPosY = packet.stDefMsg.Tag;
        bDir         = LoByte(packet.stDefMsg.Series);
        m_bLightSize = HiByte(packet.stDefMsg.Series);
        OnCharDescPacket(packet);

        SetMotionFrame(_MT_MON_WALK, bDir);
        m_bMoveSpeed = _SPEED_WALK;
        SetMoving();
    }

    public void OnTurn(PPacketMsg packet)
    {
        ubyte bDir;
        m_wPosX      = packet.stDefMsg.Param;
        m_wPosY      = packet.stDefMsg.Tag;
        bDir         = LoByte(packet.stDefMsg.Series);
        m_bLightSize = HiByte(packet.stDefMsg.Series);
        
        // ½ÄÀÎÃÊ.
        if ( m_stFeature.Dress == 31 )
            bDir = 0;
        
        OnCharDescPacket(packet);
        
        SetMotionFrame(_MT_MON_STAND, bDir);
        
        if ( m_nState & 0X1 )        // ¼®È­»óÅÂ.
        {
            switch ( m_stFeature.Dress )
            {
                case 83: //ÁÖ¸¶½ÅÀå.
                case 84: //ÁÖ¸¶È£¹ý.
                case 85: //ÁÖ¸¶¿Õ.
                {
                    SetMotionFrame(_MT_MON_APPEAR, 0);
                    m_dwEndFrame = m_dwFstFrame + 1;
                    m_bCurrMtn = _MT_MON_STAND;
                    break;
                }
                default: break;
            }
        }
    }

    public void OnDigup(PPacketMsg packet)
    {
        //MESSAGEBODYWL ????????
        ubyte bDir;
        m_wPosX      = packet.stDefMsg.Param;
        m_wPosY      = packet.stDefMsg.Tag;
        bDir         = LoByte(packet.stDefMsg.Series);
        m_bLightSize = HiByte(packet.stDefMsg.Series);
        OnCharDescPacket(packet);
        m_bReverse = false;

        Magic pxMagic;
        switch ( m_stFeature.Dress )
        {
            case 3: 
                m_bReverse = true;
                break;
            case 85: //ÁÖ¸¶¿Õ.
                bDir = 0;
                break;
            case 31: //½ÄÀÎÃÊ.      
                bDir = 0;
                m_bReverse = true;
                break;
            case 56:
            {
                pxMagic = new Magic;
                pxMagic.CreateMagic(_SKILL_SKELLETON, m_wPosX, m_wPosY, m_wPosX, m_wPosY, null, 0);
                PlayScene.MagicList.add(pxMagic);  
                break;
            }
            /*case 89:
            {
                pxMagic = new Magic;
                pxMagic.CreateMagic(_SKILL_SINSU, m_wPosX, m_wPosY, m_wPosX, m_wPosY, null, 0);
                PlayScene.MagicList.add(pxMagic);  
                break;
            }*/
            default: break;
        }
        
        m_bAppearState = _DIG_UP;
        SetMotionFrame(_MT_MON_APPEAR, bDir);
    }

    public void OnDigDown(PPacketMsg packet)
    {
        ubyte bDir;
        m_wPosX      = packet.stDefMsg.Param;
        m_wPosY      = packet.stDefMsg.Tag;
        bDir         = m_bCurrDir;
        m_bReverse   = true;
        
        switch ( m_stFeature.Dress )
        {
            case 3: 
                m_bReverse = false;
                break;
            case 31: //½ÄÀÎÃÊ.      
                bDir = 0;
                m_bReverse = false;
                break;
            default: break;
        }
        
        m_bAppearState = _DIG_DOWN;
        
        SetMotionFrame(_MT_MON_APPEAR, bDir);
    }

    public void OnDeath(PPacketMsg packet)
    {
        ubyte bDir;
        m_wPosX      = packet.stDefMsg.Param;
        m_wPosY      = packet.stDefMsg.Tag;
        bDir         = cast(ubyte)packet.stDefMsg.Series;
        OnCharDescPacket(packet);
        SetMotionFrame(_MT_MON_DIE, bDir);

        if ( packet.stDefMsg.Ident == SM_DEATH )         
        {
            m_dwCurrFrame = m_dwEndFrame - 1;
            m_bIsDead = true;
        }
        else 
        {
            // ¸ó½ºÅÍÀÏ¶§.
            if ( m_stFeature.Gender == _GENDER_MON )
            {
                Magic pxMagic;
                switch ( m_stFeature.Dress )
                {
                    case 4:     // ¼®ÀåÀÎ.
                    {
                        pxMagic = new Magic;
                        pxMagic.CreateMagic(_MONMAGIC_EXPLODE1, m_wPosX, m_wPosY, m_wPosX, m_wPosY, this);
                        PlayScene.MagicList.add(pxMagic);
                        break;
                    }
                    case  5:        // ¹ÙÄí°¡¸£³ª.
                    case  6:        // ¹ÙÀÚ¿ï.
                    case 11:        // ¸ðµðÁ¯.
                    case 19:        // ·¹µð°¡¸£³ª.
                    {
                        pxMagic = new Magic;
                        pxMagic.CreateMagic(_MONMAGIC_SINGI_DIE, m_wPosX, m_wPosY, m_wPosX, m_wPosY, this);
                        PlayScene.MagicList.add(pxMagic);
                        /*LoadEffect(&g_xGameProc.m_xImage, _MONMAGIC_DIE);
                        m_bUseSwordEffect = true;*/
                        break;
                    }
                    case 40:        // Çã¼ö¾Æºñ.
                    {
                        pxMagic = new Magic;
                        pxMagic.CreateMagic(_MONMAGIC_HUSU_DIE, m_wPosX, m_wPosY, m_wPosX, m_wPosY, this);
                        PlayScene.MagicList.add(pxMagic);
                        /* LoadEffect(&g_xGameProc.m_xImage, _MONMAGIC_HUSU_DIE);
                        m_bUseSwordEffect = true;*/
                        break;
                    }
                    case 48:        // Á»ºñ.
                    {
                        pxMagic = new Magic;
                        pxMagic.CreateMagic(_MONMAGIC_ZOMBIE_DIE, m_wPosX, m_wPosY, m_wPosX, m_wPosY, this);
                        PlayScene.MagicList.add(pxMagic);
                        /*LoadEffect(&g_xGameProc.m_xImage, _MONMAGIC_ZOMBIE_DIE);
                        m_bUseSwordEffect = true;*/
                        break;
                    }
                    case 99:        // ¹Ù¿À´Þµå.
                    {
                        pxMagic = new Magic;
                        pxMagic.CreateMagic(_MONMAGIC_BAODIE, m_wPosX, m_wPosY, m_wPosX, m_wPosY, this);
                        PlayScene.MagicList.add(pxMagic);
                        break;
                    }
                    default: break;
                }
            }
        }   
    }

    public void OnBackStep(PPacketMsg packet)
    {
        ubyte bDir;
        m_wPosX      = packet.stDefMsg.Param;
        m_wPosY      = packet.stDefMsg.Tag;
        bDir         = cast(ubyte)packet.stDefMsg.Series;
        SetMotionFrame(_MT_MON_WALK, bDir);
        if ( m_bCurrDir < 4 ) m_bMoveDir = cast(ubyte)(m_bCurrDir + 4);
        else m_bMoveDir = cast(ubyte)(m_bCurrDir - 4);
        m_bMoveSpeed = _SPEED_WALK;
        m_bBackStepFrame    = 0;
        m_bBackStepFrameCnt = 6;
        SetBackStepMoving();
    }

    public void OnStruck(PPacketMsg packet)
    {
        MessageBodyWL stMsgBodyWl;
        Feature       stFeature;
        
        //FLOAT wHPRate = (FLOAT)((FLOAT)packet.stDefMsg.Param/(FLOAT)packet.stDefMsg.Tag);
        ushort wDamage = packet.stDefMsg.Series;
        m_wHP         = packet.stDefMsg.Param;
        m_wMAXHP      = packet.stDefMsg.Tag;
        //m_bHPPercent  = wHPRate*100;
        m_bHPPercent  = cast(ubyte)get_percentage(packet.stDefMsg.Param, packet.stDefMsg.Tag);
        
        DecodeBuffer(packet.szEncodeData, &stMsgBodyWl, stMsgBodyWl.sizeof);
        
        memcpy(&stFeature, &stMsgBodyWl.Param1, stMsgBodyWl.Param1.sizeof);
        
        ChangeFeature(stFeature);
        SetMotionFrame(_MT_MON_HITTED, m_bCurrDir);
        
        // ¸ó½ºÅÍÀÏ¶§.
        if ( m_stHitter.Gender == _GENDER_MON )
        {
            Magic pxMagic;
            switch ( m_stHitter.Dress )
            {
                case 2:     // ÄÉÆÈ·ÎÇÁ.
                {
                    pxMagic = new Magic;
                    pxMagic.CreateMagic(_MONMAGIC_KEPAL, m_wPosX, m_wPosY, m_wPosX, m_wPosY, this);
                    PlayScene.MagicList.add(pxMagic);
                    break;
                }
                case 8:     // °©ÁÖ°³¹Ì.
                case 14:    // º´¿ë°³¹Ì.
                case 16:    // µå³­°³¹Ì.
                {
                    pxMagic = new Magic;
                    pxMagic.CreateMagic(_MONMAGIC_GREATANT, m_wPosX, m_wPosY, m_wPosX, m_wPosY, this);
                    PlayScene.MagicList.add(pxMagic);
                    break;
                }
                case 67:    // ÃË·æ½Å.
                {
                    pxMagic = new Magic;
                    pxMagic.CreateMagic(_MONMAGIC_BIGGINE_CHAR, m_wPosX, m_wPosY, m_wPosX, m_wPosY, this);
                    PlayScene.MagicList.add(pxMagic);
                    break;
                }
                default: break;
            }
        }
        m_stHitter = m_stHitter.init;
    }

    public void OnHit(PPacketMsg packet)
    {
        ubyte bDir;
        bDir    = cast(ubyte)packet.stDefMsg.Series;
        m_wPosX = packet.stDefMsg.Param;
        m_wPosY = packet.stDefMsg.Tag;
        SetMotionFrame(_MT_MON_ATTACK_A, bDir);
        
        // ¸ó½ºÅÍÀÏ¶§.
        if ( m_stFeature.Gender == _GENDER_MON )
        {
            Magic pxMagic;
            switch ( m_stFeature.Dress )
            {
                case 9:     // ·Ð.
                    pxMagic = new Magic;
                    pxMagic.CreateMagic(_MONMAGIC_EXPLODE, m_wPosX, m_wPosY, m_wPosX, m_wPosY, this);
                    PlayScene.MagicList.add(pxMagic);
                    break;
                case 5:     // ¹ÙÄí°¡¸£³ª.
                    pxMagic = new Magic;
                    pxMagic.CreateMagic(_MONMAGIC_BLACK1, m_wPosX, m_wPosY, m_wPosX, m_wPosY, this);
                    PlayScene.MagicList.add(pxMagic);
                    break;
                case 19:        // ·¹µð°¡¸£³ª.
                    pxMagic = new Magic;
                    pxMagic.CreateMagic(_MONMAGIC_RED1, m_wPosX, m_wPosY, m_wPosX, m_wPosY, this);
                    PlayScene.MagicList.add(pxMagic);
                    break;
                case 45:        // ¿ì¸é±Í¿Õ. 
                    pxMagic = new Magic;
                    pxMagic.CreateMagic(_MONMAGIC_COWGHOST, m_wPosX, m_wPosY, m_wPosX, m_wPosY, this);
                    PlayScene.MagicList.add(pxMagic);
                    break;
                case 90:        // ½Å¼ö.
                    pxMagic = new Magic;
                    pxMagic.CreateMagic(_MONMAGIC_SINSU, m_wPosX, m_wPosY, m_wPosX, m_wPosY, this);
                    PlayScene.MagicList.add(pxMagic);
                    break;
                case 43:        // È­¿°¿ì¸é±Í.
                    pxMagic = new Magic;
                    pxMagic.CreateMagic(_MONMAGIC_COWFLAME, m_wPosX, m_wPosY, m_wPosX, m_wPosY, this);
                    PlayScene.MagicList.add(pxMagic);
                    break;
                case 85:        // ÁÖ¸¶¿Õ.
                    pxMagic = new Magic;
                    pxMagic.CreateMagic(_MONMAGIC_JUMAWANG, m_wPosX, m_wPosY, m_wPosX, m_wPosY, this);
                    PlayScene.MagicList.add(pxMagic);
                    break;
                case 67:        // ÃË·æ½Å.
                    pxMagic = new Magic;
                    pxMagic.CreateMagic(_MONMAGIC_BIGGINE_ATT, m_wPosX, m_wPosY, m_wPosX, m_wPosY, this);
                    PlayScene.MagicList.add(pxMagic);
                    break;  
                case 75:        // ½û±â³ª¹æ.
                    pxMagic = new Magic;
                    pxMagic.CreateMagic(_MONMAGIC_SSEGI, m_wPosX, m_wPosY, m_wPosX, m_wPosY, this);
                    PlayScene.MagicList.add(pxMagic);
                    break;  
                case 94:        // »ç¾î.
                    pxMagic = new Magic;
                    pxMagic.CreateMagic(_MONMAGIC_SANDFISH, m_wPosX, m_wPosY, m_wPosX, m_wPosY, this);
                    PlayScene.MagicList.add(pxMagic);
                    break;  
                case 98:        // »ó±Þ±«¹°.
                    m_dwCurrEffectFrame = 50;
                    m_dwFstEffectFrame  = 50;
                    m_dwEndEffectFrame  = 60;
                    m_bEffectFrame      = 0;
                    m_bEffectFrameCnt   = _DEFAULT_SPELLFRAME;
                    LoadEffect(PlayScene.m_xImage, _MONMAGIC_NUMAGUMGI, bDir);
                    m_bUseSwordEffect = true;
                    break;  
                default: break;
            }
        }
    }

    public void OnFlyAxe(PPacketMsg packet)
    {
        int nTargetX, nTargetY, nTargetID;
        Magic pxMagic;
        
        m_wPosX      = packet.stDefMsg.Param;
        m_wPosY      = packet.stDefMsg.Tag;
        m_bCurrDir   = cast(ubyte)packet.stDefMsg.Series;
        
        MessageBodyWL stMsgBodyWl;
        DecodeBuffer(packet.szEncodeData, &stMsgBodyWl, stMsgBodyWl.sizeof);
        nTargetX = stMsgBodyWl.Param1;
        nTargetY = stMsgBodyWl.Param2;
        nTargetID = MakeLong(cast(ushort)stMsgBodyWl.Tag1, cast(ushort)stMsgBodyWl.Tag2);
        
        switch ( m_stFeature.Dress )
        {
            case 33:                // ½Öµµ³¢ÇØ°ñ.
            {
                pxMagic = new Magic;
                pxMagic.CreateMagic(_MONMAGIC_DUALAXE, m_wPosX, m_wPosY, nTargetX, nTargetY, this, nTargetID);
                PlayScene.MagicList.add(pxMagic);
                SetMotionFrame(_MT_MON_ATTACK_A, m_bCurrDir);
                break;
            }
            case 46:                // ´ÙÅ©.
            {
                pxMagic = new Magic;
                pxMagic.CreateMagic(_MONMAGIC_CHIM, m_wPosX, m_wPosY, nTargetX, nTargetY, this, nTargetID);
                PlayScene.MagicList.add(pxMagic);
                SetMotionFrame(_MT_MON_ATTACK_A, m_bCurrDir);
                break;
            }
            case 82:                // ¸¶±Ã»ç.
            {
                pxMagic = new Magic;
                pxMagic.CreateMagic(_MONMAGIC_MAARROW, m_wPosX, m_wPosY, nTargetX, nTargetY, this, nTargetID);
                PlayScene.MagicList.add(pxMagic);
                SetMotionFrame(_MT_MON_ATTACK_A, m_bCurrDir);
                break;
            }
            default: break;
        }
    }

    public void OnLighting(PPacketMsg packet)
    {
        int nTargetX, nTargetY, nTargetID;
        Magic pxMagic;
        
        m_wPosX      = packet.stDefMsg.Param;
        m_wPosY      = packet.stDefMsg.Tag;
        m_bCurrDir   = cast(ubyte)packet.stDefMsg.Series;
        
        MessageBodyWL stMsgBodyWl;
        DecodeBuffer(packet.szEncodeData, &stMsgBodyWl, stMsgBodyWl.sizeof);
        nTargetX = stMsgBodyWl.Param1;
        nTargetY = stMsgBodyWl.Param2;
        nTargetID = MakeLong(cast(ushort)stMsgBodyWl.Tag1, cast(ushort)stMsgBodyWl.Tag2);
        
        switch ( m_stFeature.Dress )
        {
            case 48:        // Á»ºñ. 8¹æÇâ.
            {
                pxMagic = new Magic;
                pxMagic.CreateMagic(_MONMAGIC_ZOMBIE, m_wPosX, m_wPosY, m_wPosX, m_wPosY, this, nTargetID);
                PlayScene.MagicList.add(pxMagic);
                SetMotionFrame(_MT_MON_ATTACK_A, m_bCurrDir);
                break;
            }
            default: break;
        }
    }
    //---------------------------------------------------------------------------------------------------------------//
    
    //---------------------------------------------------------------------------------------------------------------//
    // Rendering.
    public bool DrawActor(MirMap pxMap, bool bFocused = false, bool bShadowAblended = false, 
                           bool bUseScrnPos = true, bool bDrawShadow = true)
    {
        // ÁÂÇ¥Ã³¸®.
        m_shScrnPosX = (m_wPosX - pxMap.m_shStartViewTileX) * _CELL_WIDTH + _VIEW_CELL_X_START - pxMap.m_shViewOffsetX + m_shShiftPixelX;
        m_shScrnPosY = (m_wPosY - pxMap.m_shStartViewTileY) * _CELL_HEIGHT+ _VIEW_CELL_Y_START - pxMap.m_shViewOffsetY + m_shShiftPixelY;
        
        ALLEGRO_BITMAP* bmp;
        int bmp_width, bmp_height;
        
        ImageInfo imgi;
        ubyte bShadowType;
        short shPX, shPY, shShadowPX, shShadowPY;
        
        
        if ( m_pxActorImage is null )
            return false;

        bmp = m_pxActorImage.GetCachedImageEx(m_dwCurrFrame, imgi);
        if ( bmp is null )
            return false;
        
        bmp_width = al_get_bitmap_width(bmp);
        bmp_height = al_get_bitmap_height(bmp);
        shPX = imgi.PosX;
        shPY = imgi.PosY;
        
        bShadowType = imgi.Shadow;
        shShadowPX  = imgi.ShadowPX;
        shShadowPY  = imgi.ShadowPY;
        
        m_rcActor.set(m_shScrnPosX + shPX, m_shScrnPosY + shPY, 
                      m_shScrnPosX + shPX + bmp_width, 
                      m_shScrnPosY + shPY + bmp_height);

        assignTargetRegion(m_rcActor, m_rcTargetRgn);
        
        // Ä³¸¯ÅÍ ±×¸²ÀÚ.
        int nStartX1 = m_shScrnPosX + shShadowPX;
        int nStartY1 = m_shScrnPosY + shShadowPY;
        
        ubyte bOpaRate = 70;
        ushort wState = GetCharState();
        
        // ±×¸²ÀÚ¸¦ ±×¸°´Ù.
        if ( m_wABlendDelay || wState==_STATE_ABLEND )
        {
            bShadowAblended = true;
        }

        // TODO:
        /*if ( bDrawShadow )
            g_xMainWnd.DrawWithShadowABlend(
                nStartX1, nStartY1,
                m_pxActorImage.m_lpstNewCurrWilImageInfo.shWidth,                         
                m_pxActorImage.m_lpstNewCurrWilImageInfo.shHeight,
                (WORD*)m_pxActorImage.m_pbCurrImage,
                _CLIP_WIDTH, _CLIP_HEIGHT, g_xGameProc.m_wShadowClr, bShadowAblended, bShadowType, bOpaRate);*/
        
        if ( m_wABlendDelay )
        {
            if ( !m_bABlendRev )
                bOpaRate = cast(ubyte)(100 - ( (m_wABlendCurrDelay * 100) / m_wABlendDelay ));
            else
                bOpaRate = cast(ubyte)( (m_wABlendCurrDelay * 100) / m_wABlendDelay );
        }
        
        if ( bOpaRate < 0 && bOpaRate > 100 )       bOpaRate  = 0;
        
        if ( bFocused )                             
        {
            if ( !m_bABlendRev )
                bOpaRate -= 20;
            else
                bOpaRate += 20;
        }
        
        DrawWithEffected(m_rcActor.getLeft(), m_rcActor.getTop(), bmp_width, bmp_height, bmp, 
                         0XFFFF, 0XFFFF, bFocused, bOpaRate, wState);
      
        return true;
    }

    public void DrawHPBar()
    {
        const ALLEGRO_COLOR BarBackColor = al_map_rgba(0, 0, 0, 255);
        const ALLEGRO_COLOR BarHealthColor = al_map_rgba(255, 0, 0, 255);

        if ( !m_bOpenHealth ) return;

        ALLEGRO_BITMAP* bmp;
        float fHPPercent = cast(float)(cast(float)m_bHPPercent / 100.0);
        float fHPWidth = (36 * fHPPercent);

        immutable int hx = m_shScrnPosX + 7;
        immutable int hy = m_shScrnPosY - 53;
        immutable int hw = 32;
        immutable int hh = 4;

        al_draw_filled_rectangle(hx, hy, hx + hw, hy + hh, BarBackColor);
        al_draw_filled_rectangle(hx, hy, hx + fHPWidth, hy + hh, BarHealthColor);
    }

    public void DrawName()
    {
        const ALLEGRO_COLOR NameBackColor = al_map_rgba(10, 10, 10, 255);
        IntRectangle rc, rcLeft;
        IntPoint pt = MyPen.measureText(m_szName);


        rc.set( m_rcActor.getLeft() + (m_rcActor.getWidth()-pt.getX())/2, m_rcActor.getTop() + (m_rcActor.getHeight()-pt.getY())/2, 
            m_rcActor.getLeft() + (m_rcActor.getWidth()+pt.getX())/2, m_rcActor.getTop() + (m_rcActor.getHeight()+pt.getY())/2 );  
        rcLeft.set( rc.getLeft()+1, rc.getTop()-1, rc.getRight()+1, rc.getBottom()-1 );
        MyPen.drawText(rc, NameBackColor, AlTransparentBlack, m_szName);
        MyPen.drawText(rcLeft, m_dwNameClr, AlTransparentBlack, m_szName);
    }
    
    public bool DrawEffect()
    {
        ALLEGRO_BITMAP* bmp;
        ImageInfo imgi;

        if ( m_bUseEffect || m_bUseSwordEffect )
        {
            if ( !m_bUseEffect )
            {
                m_dwCurrEffectFrame = m_dwFstEffectFrame + m_dwCurrFrame - m_dwFstFrame;
            }

            bmp = PlayScene.m_xImage[m_bEffectImgIdx].GetCachedImageEx(m_dwCurrEffectFrame, imgi);

            if ( bmp !is null )
            {
                immutable float effx = cast(float)m_shScrnPosX + cast(float)imgi.Width / 2 + imgi.PosX-400;
                immutable float effy = cast(float)m_shScrnPosY - cast(float)imgi.Height / 2 + imgi.PosY+300;

                al_draw_bitmap(bmp, effx, effy, 0);
            }
            
            
            if ( m_bUseEffect )
            {
                if ( m_bEffectFrame < m_bEffectFrameCnt / 2 )
                {
                    m_bLightRadius[0] = m_bEffectFrame;
                    m_bLightRadius[1] = cast(ubyte)(m_bEffectFrame+1);
                }
                else
                {
                    m_bLightRadius[0] = cast(ubyte)(m_bEffectFrameCnt - m_bEffectFrame-1);
                    m_bLightRadius[1] = cast(ubyte)(m_bEffectFrameCnt - m_bEffectFrame);
                }
                
                PlayScene.m_xLightFog.SetLightRadiusWithRing(
                    m_shScrnPosX + _CELL_WIDTH/2,
                    m_shScrnPosY + _CELL_HEIGHT/2, 
                    m_bLightRadius[0], m_bLightColor[0][0], m_bLightColor[0][1], m_bLightColor[0][2], 
                    m_bLightRadius[1], m_bLightColor[1][0], m_bLightColor[1][1], m_bLightColor[1][2]);
            }
            else if ( m_bUseSwordEffect )
            {
                if ( m_dwEndEffectFrame - m_dwFstEffectFrame > 8 )
                {
                    m_bLightRadius[0] = cast(ubyte)((m_dwCurrEffectFrame - m_dwFstEffectFrame)/2 + 2);
                    m_bLightRadius[1] = cast(ubyte)((m_dwCurrEffectFrame - m_dwFstEffectFrame)/2 + 3);
                }
                else
                {
                    m_bLightRadius[0] = cast(ubyte)(m_dwCurrEffectFrame - m_dwFstEffectFrame + 2);
                    m_bLightRadius[1] = cast(ubyte)(m_dwCurrEffectFrame - m_dwFstEffectFrame + 3);
                }
                PlayScene.m_xLightFog.SetLightRadiusWithRing(
                    m_shScrnPosX + _CELL_WIDTH/2,
                    m_shScrnPosY + _CELL_HEIGHT/2, 
                    m_bLightRadius[0], m_bLightColor[0][0], m_bLightColor[0][1], m_bLightColor[0][2], 
                    m_bLightRadius[1], m_bLightColor[1][0], m_bLightColor[1][1], m_bLightColor[1][2]);
            }

            return true;
        }

        return false;
    }
}




public class NPC : Actor
{
    public override void OnHit(PPacketMsg packet)
    {
        ubyte bDir;
        bDir    = cast(ubyte)packet.stDefMsg.Series;
        m_wPosX = packet.stDefMsg.Param;
        m_wPosY = packet.stDefMsg.Tag;
        SetMotionFrame(_MT_NPC_ACT01, bDir);
    }

    public override void OnTurn(PPacketMsg packet)
    {
        ubyte bDir;
        m_wPosX = packet.stDefMsg.Param;
        m_wPosY = packet.stDefMsg.Tag;
        bDir         = LoByte(packet.stDefMsg.Series);
        m_bLightSize = HiByte(packet.stDefMsg.Series);
        OnCharDescPacket(packet);
        SetMotionFrame(_MT_NPC_STAND, bDir);
    }

    public override bool UpdatePacketState()
    {
        PPacketMsg packet = null;

        //  if ( m_bCurrMtn == _MT_NPC_STAND )
        {
            int nLeftMsgCnt = m_xPacketQueue.count();
            
            if ( nLeftMsgCnt > 0 )
            {
                packet = m_xPacketQueue.dequeue();
                
                if ( nLeftMsgCnt >= 3 ) m_bMsgHurryCheck = true;
                else m_bMsgHurryCheck = false;
                
                if ( packet !is null )
                {
                    switch ( packet.stDefMsg.Ident )
                    {
                        case SM_OPENHEALTH:         {   OnOpenHealth(packet);          break;  }
                        case SM_CLOSEHEALTH:        {   OnCloseHealth(packet);         break;  }
                        case SM_CHANGELIGHT:        {   OnChangeLight(packet);         break;  }
                        case SM_USERNAME:           {   OnUserName(packet);            break;  }
                        case SM_CHANGENAMECOLOR:    {   OnChangeNameClr(packet);       break;  }
                        case SM_HEALTHSPELLCHANGED: {   OnHealthSpellChanged(packet);  break;  }
                        case SM_TURN:               {   OnTurn(packet);                break;  }
                        case SM_HIT:                {   OnHit(packet);                 break;  }
                        default: break;  
                    }
                    destroy(packet);
                }

                return true;
            }
        }
        return false;
    }

    public override void UpdateMotionState(int nLoopTime, bool bIsMoveTime)
    {
        m_wCurrDelay += nLoopTime;
        
        if ( m_wCurrDelay > m_wDelay )
        {
            m_wCurrDelay = 0;
            
            if ( m_dwCurrFrame < m_dwEndFrame ) m_dwCurrFrame++;
        }
        
        UpdatePacketState();
        
        if ( m_dwCurrFrame >= m_dwEndFrame ) m_dwCurrFrame = m_dwFstFrame;
    }

    public override bool DrawActor(MirMap pxMap, bool bFocused = false, bool bShadowAblended = false, 
                                   bool bUseScrnPos = true, bool bDrawShadow = true)
    {
        // ÁÂÇ¥Ã³¸®.
        m_shScrnPosX = (m_wPosX - pxMap.m_shStartViewTileX) * _CELL_WIDTH + _VIEW_CELL_X_START - pxMap.m_shViewOffsetX + m_shShiftPixelX;
        m_shScrnPosY = (m_wPosY - pxMap.m_shStartViewTileY) * _CELL_HEIGHT+ _VIEW_CELL_Y_START - pxMap.m_shViewOffsetY + m_shShiftPixelY;

        ALLEGRO_BITMAP* bmp;
        int bmp_width, bmp_height;

        ImageInfo imgi;
        ubyte bShadowType;
        short shPX, shPY, shShadowPX, shShadowPY;

        
        if ( m_pxActorImage is null )
            return false;

        bmp = m_pxActorImage.GetCachedImageEx(m_dwCurrFrame, imgi);
        if ( bmp is null )
            return false;

        bmp_width = al_get_bitmap_width(bmp);
        bmp_height = al_get_bitmap_height(bmp);
        shPX = imgi.PosX;
        shPY = imgi.PosY;
        
        bShadowType = imgi.Shadow;
        shShadowPX  = imgi.ShadowPX;
        shShadowPY  = imgi.ShadowPY;
        
        m_rcActor.set(m_shScrnPosX + shPX, m_shScrnPosY + shPY, 
                      m_shScrnPosX + shPX + bmp_width, 
                      m_shScrnPosY + shPY + bmp_height);

        assignTargetRegion(m_rcActor, m_rcTargetRgn);
        
        // Ä³¸¯ÅÍ ±×¸²ÀÚ.
        int nStartX1 = m_shScrnPosX + shShadowPX;
        int nStartY1 = m_shScrnPosY + shShadowPY;
        
        ubyte bOpaRate = 70;
        ushort wState = GetCharState();
        
        // ±×¸²ÀÚ¸¦ ±×¸°´Ù.
        if ( m_wABlendDelay || wState==_STATE_ABLEND )
        {
            bShadowAblended = true;
        }

        // TODO:
        /*if ( bDrawShadow )
            g_xMainWnd.DrawWithShadowABlend(
                nStartX1, nStartY1,
                m_pxActorImage.m_lpstNewCurrWilImageInfo.shWidth,                         
                m_pxActorImage.m_lpstNewCurrWilImageInfo.shHeight,
                (WORD*)m_pxActorImage.m_pbCurrImage,
                _CLIP_WIDTH, _CLIP_HEIGHT, g_xGameProc.m_wShadowClr, bShadowAblended, bShadowType, bOpaRate);*/
        
        if ( m_wABlendDelay )
        {
            if ( !m_bABlendRev )
                bOpaRate = cast(ubyte)(100 - ( (m_wABlendCurrDelay * 100) / m_wABlendDelay ));
            else
                bOpaRate = cast(ubyte)( (m_wABlendCurrDelay * 100) / m_wABlendDelay );
        }
        
        if ( bOpaRate < 0 && bOpaRate > 100 )       bOpaRate  = 0;
        
        if ( bFocused )                             
        {
            if ( !m_bABlendRev )
                bOpaRate -= 20;
            else
                bOpaRate += 20;
        }
        
        DrawWithEffected(m_rcActor.getLeft(), m_rcActor.getTop(), bmp_width, bmp_height, bmp, 
                         0XFFFF, 0XFFFF, bFocused, bOpaRate, wState);
        
        return true;
    }
}




struct FeatureEx
{
    public ubyte Horse;
    public ubyte HairColor;
    public ubyte DressColor;
}
alias FeatureEx* PFeatureEx;

public class Hero : Actor
{
    public FeatureEx m_stFeatureEx;
    
    public int m_bWeaponImgIdx;               // ÇöÀç Actor ¹«±â°¡ °¡Áú ÀÌ¹ÌÁö±¸Á¶Ã¼ÀÇ ¹øÈ£.
    public int m_bHairImgIdx;                 // ÇöÀç Actor ¸Ó¸®°¡ °¡Áú ÀÌ¹ÌÁö±¸Á¶Ã¼ÀÇ ¹øÈ£.
    public int m_bHorseImgIdx;                // ÇöÀç Actor ¸»ÀÌ   °¡Áú ÀÌ¹ÌÁö±¸Á¶Ã¼ÀÇ ¹øÈ£.
    
    public ubyte m_bYedoCnt;
    public ubyte m_bFireHitCnt;
    public bool m_bUseBanwol;
    public bool m_bUseErgum;
    
    public IntRectangle m_rcHair;
    public IntRectangle m_rcWeapon;
    public IntRectangle m_rcHorse;
    
    public ImageArray m_pxHairImage;      
    public ImageArray m_pxWeaponImage;
    public ImageArray m_pxHorseImage;
    
    public uint m_dwCurrHairFrame;
    public uint m_dwCurrWeaponFrame;
    public uint m_dwCurrHorseFrame;
    
    public ubyte m_bShieldCurrFrm;
    public ushort m_wShieldCurrDelay;
    
    public short m_shHitSpeed;
    
    public bool m_bIsMon;
    
    public int m_shCurrMagicID;



    public this()
    {
        InitActor();
    }

    public ~this()
    {
        DestroyActor();
    }
    
    public override void InitActor()
    {
        super.InitActor();
        
        m_stFeatureEx   = m_stFeatureEx.init;
        
        m_bWeaponImgIdx = 0;
        m_bHairImgIdx = 0;    
        m_bHorseImgIdx = 0;    
        m_bYedoCnt = 0;
        m_bFireHitCnt = 0;
        m_shHitSpeed = 0;
        
        m_pxHairImage = null;
        m_pxWeaponImage = null;
        m_pxHorseImage = null;
        
        m_bUseBanwol = false;
        m_bUseErgum = false;
        m_bIsMon = false;
        
        m_bShieldCurrFrm = 0;
        m_wShieldCurrDelay = 0;
        m_dwCurrHairFrame = 0;
        m_dwCurrWeaponFrame = 0;
        m_dwCurrHorseFrame = 0;
        m_shCurrMagicID = 0;

        m_rcHair = IntRectangle.zero;
        m_rcWeapon = IntRectangle.zero;
        m_rcHorse = IntRectangle.zero;
    }

    public override void DestroyActor()
    {
        super.DestroyActor();
        InitActor();
    }
    
    public void ShowShield()
    {
        const ALLEGRO_COLOR tint_color = al_map_rgba(255, 255, 255, 0);

        if ( m_nState & 0X100000 )
        {
            int wShieldFrm;
            if ( m_bCurrMtn == _MT_HITTED ) wShieldFrm = m_bShieldCurrFrm + 853;
            else wShieldFrm = m_bShieldCurrFrm + 850;


            ImageInfo imgi;
            ALLEGRO_BITMAP* bmp;

            bmp = PlayScene.m_xImage[_IMAGE_MAGIC].GetCachedImageEx(wShieldFrm, imgi);
            if ( bmp !is null )
            {
                int bmp_width = al_get_bitmap_width(bmp);
                int bmp_height = al_get_bitmap_height(bmp);
                float posX = cast(float)m_shScrnPosX+cast(float)bmp_width/2+imgi.PosX-400; 
                float posY = cast(float)-m_shScrnPosY-cast(float)bmp_height/2-imgi.PosY+300;

                al_draw_tinted_bitmap(bmp, tint_color, posX, posY, 0);
            }
        }
    }

    public void PlayMoveSound()
    {
        if ( m_bCurrMtn == _MT_WALK || m_bCurrMtn == _MT_RUN || m_bCurrMtn == _MT_HORSEWALK || m_bCurrMtn == _MT_HORSERUN || m_bCurrMtn == _MT_MOODEPO || m_bCurrMtn == _MT_PUSHBACK )
        {
            /*      if ( m_dwCurrFrame == m_dwEndFrame-5 || m_bBackStepFrame == m_dwEndFrame-5 )
            g_xSound.PlayActorWav(m_wPosX, m_wPosY, g_xGameProc.m_xMyHero.m_wPosX, g_xGameProc.m_xMyHero.m_wPosY, 1,100);
            if ( m_dwCurrFrame == m_dwEndFrame-2 || m_bBackStepFrame == m_bBackStepFrameCnt-2 )
                g_xSound.PlayActorWav(m_wPosX, m_wPosY, g_xGameProc.m_xMyHero.m_wPosX, g_xGameProc.m_xMyHero.m_wPosY, 1,100);*/
        }
    }

    public override void PlayActSound()
    {
        int nWaveNum = -1;
        
        if ( m_dwCurrFrame == m_dwFstFrame+1 && m_bCurrMtn == _MT_HITTED )
        {
            switch ( m_stHitter.Weapon )
            {
                case 21: case 24: // ´Ü°Ë, ºñ´Ü°Ë.
                case 8: case 9: // ¸ñ°Ë, ¾Æ¸®¼ö¸ñ°Ë.
                case 18:  case 22:  case 23:  case 26:  case 27:  case 28:  case 30:  // »ç¸ð°Ë. Ã»µ¿°Ë. Ã¶°Ë. Ã»À½°Ë. º®»ç°Ë. Ãµ·É. °î¼º°Ë.
                case 1:   case 4:  case 11:  case 13:  case 14:  case 20:  case 25:  case 29:  case 31:   // À¯¿ùµµ. ¹¬Ã»´ëµµ. À°ÇÕµµ. ±ºµµ. µµ·æº¸µµ. »ç°¢µµ. ¼¼Ã·µµ. ¿¹µµ. ÃÊÈ¥µµ.
                    nWaveNum = 70;
                    break;
                case 5:  case 10:  case 12: // »ïÀû´ëºÎ. Ã»µ¿µµ³¢. ¿¬ÀÚºÎ.
                case 15:  // ÆÄ³úÁø´ç.
                    nWaveNum = 71;
                    break;
                case 2:  case 3:  case 6:  case 7: case 16:  case 17: case 19:  // »ïÁöÃ¢. ÃµÇü¸ñ. È«¾ÆÃ¢. °î±ªÀÌ. Ã»¸¶Ã¢, ¿ë¾ÆÀå. Á¦¸¶ºÀ
                    nWaveNum = 72;
                    break;
                default:
                    nWaveNum = 73;  // ¸Ç¼Õ.
                    break;
            }
            if ( m_stFeature.Dress == 3 )      // ¹Ì°©ÁÖ.
                nWaveNum += 10;
        }
        else if ( m_dwCurrFrame == m_dwFstFrame+2 && m_bCurrMtn == _MT_HITTED )
        {
            if ( m_stFeature.Gender == _GENDER_MAN )   nWaveNum = 138;
            else                                        nWaveNum = 139;
        }
        else if ( m_dwCurrFrame == m_dwFstFrame+1 && m_bCurrMtn == _MT_DIE )
        {
            if ( m_stFeature.Gender == _GENDER_MAN )   nWaveNum = 144;
            else                                        nWaveNum = 145;
            
        }
        else if ( m_dwCurrFrame == m_dwFstFrame+1 && m_bCurrMtn >= _MT_ONEVSWING && m_bCurrMtn <= _MT_SPEARHSWING )
        {
            if ( m_bFireHitCnt == 2 )
            {
                nWaveNum = 137;
            }
            else if ( m_bYedoCnt == 2 )
            {
                // TODO:
                /*if ( m_stFeature.Gender == _GENDER_MAN )
                {
                    g_xSound.PlayActorWav(m_wPosX, m_wPosY, g_xGameProc.m_xMyHero.m_wPosX, g_xGameProc.m_xMyHero.m_wPosY, 130);
                }
                else
                {
                    g_xSound.PlayActorWav(m_wPosX, m_wPosY, g_xGameProc.m_xMyHero.m_wPosX, g_xGameProc.m_xMyHero.m_wPosY, 131);
                }*/
                m_bYedoCnt = 0;
            }
            else if ( m_bUseBanwol )
            {
                nWaveNum = 133;
            }
            else if ( m_bUseErgum )
            {
                nWaveNum = 132;
            }
        }
        else if ( m_dwCurrFrame == m_dwFstFrame+2 && m_bCurrMtn >= _MT_ONEVSWING && m_bCurrMtn <= _MT_SPEARHSWING )
        {
            switch ( m_stFeature.Weapon )
            {
                case 21: case 24: // ´Ü°Ë, ºñ´Ü°Ë.
                    nWaveNum = 50;
                    break;
                case 8: case 9: // ¸ñ°Ë, ¾Æ¸®¼ö¸ñ°Ë.
                    nWaveNum = 51;
                    break;
                case 18:  case 22:  case 23:  case 26:  case 27:  case 28:  case 30:  // »ç¸ð°Ë. Ã»µ¿°Ë. Ã¶°Ë. Ã»À½°Ë. º®»ç°Ë. Ãµ·É. °î¼º°Ë.
                    nWaveNum = 52;
                    break;
                case 1:   case 4:  case 11:  case 13:  case 14:  case 20:  case 25:  case 29:  case 31:   // À¯¿ùµµ. ¹¬Ã»´ëµµ. À°ÇÕµµ. ±ºµµ. µµ·æº¸µµ. »ç°¢µµ. ¼¼Ã·µµ. ¿¹µµ. ÃÊÈ¥µµ.
                    nWaveNum = 53;
                    break;
                case 5:  case 10:  case 12: // »ïÀû´ëºÎ. Ã»µ¿µµ³¢. ¿¬ÀÚºÎ.
                    nWaveNum = 54;
                    break;
                case 15:  // ÆÄ³úÁø´ç.
                    nWaveNum = 55;
                    break;
                case 2:  case 3:  case 6:  case 7: case 16:  case 17: case 19:  // »ïÁöÃ¢. ÃµÇü¸ñ. È«¾ÆÃ¢. °î±ªÀÌ. Ã»¸¶Ã¢, ¿ë¾ÆÀå. Á¦¸¶ºÀ
                    nWaveNum = 56;
                    break;
                default:
                    nWaveNum = 57;  // ¸Ç¼Õ.
                    break;
            }
        }
        else if ( m_dwCurrFrame == m_dwFstFrame+1 && (m_bCurrMtn == _MT_SPELL1 || m_bCurrMtn == _MT_SPELL2) )
        {
            const int nMagicSndTbl[31] = 
            [
                0,  6,  8, 14, 15,  9,  0, 20, 21, 32,
                1,  2, 16, 29, 24, 10, 17, 33, 19, 31,
                28, 22, 13, 21,  5,  0, 12, 11,  0, 18, 
                30
            ];
            if ( m_shCurrMagicID < 31 )
            {
                nWaveNum = nMagicSndTbl[m_shCurrMagicID]*10 + 10000;
            }
            if ( nWaveNum < 10010 )
                nWaveNum = -1;
        }

        // TODO:
        /*  if ( nWaveNum != -1 )
        g_xSound.PlayActorWav(m_wPosX, m_wPosY, g_xGameProc.m_xMyHero.m_wPosX, g_xGameProc.m_xMyHero.m_wPosY, nWaveNum);
*/
    }

    public bool ChangeFeature(Feature stFeature, FeatureEx stFeatureEx)
    {
        if ( super.ChangeFeature(stFeature) )
        {
            ImageHandler handler = PlayScene.m_xImage;
            
            m_stFeatureEx = stFeatureEx;

            
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            //      m_stFeatureEx.bHorse = 0;
            //      m_stFeatureEx.wDressColor = 0XFFFF;
            //      m_stFeatureEx.wHairColor = 0XFFFF;
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

            if ( m_stFeature.Hair >= _MAX_HAIR ) m_stFeature.Hair = _HAIR_NONE;
            if ( m_stFeature.Weapon >= _MAX_WEAPON ) m_stFeature.Weapon = _WEAPON_NONE;
            if ( m_stFeatureEx.Horse >= _MAX_HORSE ) m_stFeatureEx.Horse= _HORSE_NONE;
            
            m_bHorseImgIdx = _IMAGE_HORSE;
            if ( m_stFeature.Gender == _GENDER_MAN )
            {
                m_bHairImgIdx = _IMAGE_M_HAIR;
                if ( m_stFeature.Weapon != _WEAPON_NONE ) m_bWeaponImgIdx = _IMAGE_M_WEAPON1+(m_stFeature.Weapon-1)/10;
            }
            else if ( m_stFeature.Gender == _GENDER_WOMAN )
            {
                m_bHairImgIdx = _IMAGE_WM_HAIR;
                if ( m_stFeature.Weapon != _WEAPON_NONE ) m_bWeaponImgIdx = _IMAGE_WM_WEAPON1+(m_stFeature.Weapon-1)/10;
            }
            else
                return false;
            
            if ( m_stFeature.Hair != _HAIR_NONE ) m_pxHairImage = handler[m_bHairImgIdx];
            else m_pxHairImage = null;
            
            if ( m_stFeature.Weapon != _WEAPON_NONE ) m_pxWeaponImage = handler[m_bWeaponImgIdx];
            else m_pxWeaponImage = null;
            
            if ( m_stFeatureEx.Horse != _HORSE_NONE ) m_pxHorseImage = handler[m_bHorseImgIdx];
            else m_pxHorseImage = null;
            
            return true;
        }
        return false;
    }

    public bool Create(ImageHandler handler, ubyte bMtn, ubyte bDir, ushort wPosX, ushort wPosY, PFeature pstFeature, PFeatureEx pstFeatureEx)
    {
        if ( super.Create(handler, pstFeature, bMtn, bDir, wPosX, wPosY) )
        {
            m_bIsMon = false;
            
            //      memcpy(&m_stFeatureEx, pstFeatureEx, sizeof(FEATUREEX));
            
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            m_stFeatureEx.Horse = 0;
            m_stFeatureEx.DressColor = 0XFF;
            m_stFeatureEx.HairColor = 0XFF;
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            
            
            if ( m_stFeature.Hair >= _MAX_HAIR ) m_stFeature.Hair = _HAIR_NONE;
            if ( m_stFeature.Weapon >= _MAX_WEAPON ) m_stFeature.Weapon = _WEAPON_NONE;
            if ( m_stFeatureEx.Horse >= _MAX_HORSE ) m_stFeatureEx.Horse = _HORSE_NONE;
            
            m_bHorseImgIdx = _IMAGE_HORSE;
            if ( m_stFeature.Gender == _GENDER_MAN )
            {
                m_bHairImgIdx = _IMAGE_M_HAIR;
                
                if ( m_stFeature.Weapon != _WEAPON_NONE ) m_bWeaponImgIdx = _IMAGE_M_WEAPON1+(m_stFeature.Weapon-1)/10;
            }
            else if ( m_stFeature.Gender == _GENDER_WOMAN )
            {
                m_bHairImgIdx = _IMAGE_WM_HAIR;
                if ( m_stFeature.Weapon != _WEAPON_NONE ) m_bWeaponImgIdx = _IMAGE_WM_WEAPON1+(m_stFeature.Weapon-1)/10;
            }
            else
                return false;
            
            if ( m_stFeature.Hair != _HAIR_NONE ) m_pxHairImage = handler[m_bHairImgIdx];
            else m_pxHairImage = null;
            
            if ( m_stFeature.Weapon != _WEAPON_NONE ) m_pxWeaponImage = handler[m_bWeaponImgIdx];
            else m_pxWeaponImage = null;
            
            if ( m_stFeatureEx.Horse != _HORSE_NONE ) m_pxHorseImage = handler[m_bHorseImgIdx];
            else m_pxHorseImage = null;
            
            return true;
        }
        return false;
    }

    public override bool UpdatePacketState()
    {
        PPacketMsg packet = null;

        if ( m_bCurrMtn == _MT_STAND || m_bCurrMtn == _MT_ATTACKMODE || m_bCurrMtn == _MT_HORSESTAND || (m_bCurrMtn==_MT_SPELL1 && m_dwCurrFrame == m_dwEndFrame-2) )
        {
            int nLeftMsgCnt = m_xPacketQueue.count();
            
            if ( nLeftMsgCnt > 0 )
            {
                packet = m_xPacketQueue.dequeue();
                
                if ( nLeftMsgCnt >= 3 ) m_bMsgHurryCheck = true;
                else m_bMsgHurryCheck = false;
                
                if ( packet !is null )
                {
                    switch ( packet.stDefMsg.Ident )
                    {
                        case SM_SITDOWN:
                        case SM_BUTCH:              {   OnButch(packet);               break;  }
                        case SM_FEATURECHANGED:     {   OnFeatureChanged(packet);      break;  }
                        case SM_CHARSTATUSCHANGED:  {   OnCharStatusChanged(packet);   break;  }
                        case SM_OPENHEALTH:         {   OnOpenHealth(packet);          break;  }
                        case SM_CLOSEHEALTH:        {   OnCloseHealth(packet);         break;  }
                        case SM_CHANGELIGHT:        {   OnChangeLight(packet);         break;  }
                        case SM_USERNAME:           {   OnUserName(packet);            break;  }
                        case SM_CHANGENAMECOLOR:    {   OnChangeNameClr(packet);       break;  }
                        case SM_HEALTHSPELLCHANGED: {   OnHealthSpellChanged(packet);  break;  }
                        case SM_RUSH:               {   OnRush(packet);                break;  }
                        case SM_BACKSTEP:           {   OnBackStep(packet);            break;  }
                        case SM_NOWDEATH:
                        case SM_DEATH:              {   OnDeath(packet);               break;  }
                        case SM_WALK:               {   OnWalk(packet);                break;  }
                        case SM_RUN:                {   OnRun(packet);                 break;  }
                        case SM_TURN:               {   OnTurn(packet);                break;  }
                        case SM_STRUCK:             {   OnStruck(packet);              break;  }
                        case SM_HIT:
                        case SM_FIREHIT:
                        case SM_LONGHIT:
                        case SM_POWERHIT:
                        case SM_WIDEHIT:            {   OnHit(packet);                 break;  }
                        case SM_MAGICFIRE:          {   OnMagicFire(packet);           break;  }
                        case SM_SPELL:              {   OnSpell(packet);               break;  }
                        default: break;  
                    }
                    destroy(packet);
                }

                return true;
            }
        }
        return false;
    }

    public override void UpdateMotionState(int nLoopTime, bool bIsMoveTime)
    {
        if ( m_bWarMode ) 
            m_dwWarModeTime += nLoopTime;
        
        m_wABlendCurrDelay += nLoopTime;
        
        if ( m_wABlendCurrDelay >= m_wABlendDelay )
        {
            m_wABlendCurrDelay = 0;
            m_wABlendDelay  = 0;
            m_bABlendRev = false;
        }
        
        if ( m_bCurrMtn == _MT_DIE && m_dwCurrFrame >= m_dwEndFrame-1 )
        {
            m_bIsDead = true;
        }
        
        if ( m_bIsDead )
        {
            SetMotionFrame(_MT_DIE, m_bCurrDir);
            m_dwCurrFrame = m_dwEndFrame - 1;
            return;
        }

        if ( UpdateMove(bIsMoveTime) )
        {
            UpdatePacketState();
            return;
        }
        else
        {
            m_wCurrDelay += nLoopTime;
            
            m_wShieldCurrDelay += nLoopTime;
            if ( m_wShieldCurrDelay > 150 )  
            {
                m_bShieldCurrFrm++;
                m_wShieldCurrDelay = 0;
                if ( m_bShieldCurrFrm > 2 )
                    m_bShieldCurrFrm = 0;
            }
            
            if ( m_wCurrDelay > m_wDelay )
            {
                m_wCurrDelay = 0;
                
                if ( m_dwCurrFrame < m_dwEndFrame )
                {
                    m_dwCurrFrame++;
                    
                    PlayActSound();
                    
                    if ( (m_bCurrMtn == _MT_SPELL2 || m_bCurrMtn == _MT_SPELL1) && m_bUseEffect )
                    {
                        m_dwCurrEffectFrame++;
                        m_bEffectFrame++;
                    }
                    
                    if ( m_bMsgHurryCheck )         
                    {
                        m_wDelay = m_wDelay/2;
                        m_bMsgHurryCheck = false;
                    }
                }
            }
            
            UpdatePacketState();
            
            if ( m_dwWarModeTime > _WARMODE_TIME )
            {
                m_dwWarModeTime = 0;
                m_bWarMode      = false;
            }
            
            if ( m_dwCurrFrame >= m_dwEndFrame-1 )
            {
                if ( (m_bCurrMtn == _MT_SPELL2) && m_bUseEffect )
                {
                    if ( m_dwCurrEffectFrame - m_dwFstEffectFrame < m_bEffectFrameCnt-2 )
                    {
                        m_dwCurrFrame = m_dwEndFrame - 2;
                    }
                }
            }
            else if ( m_dwCurrFrame >= m_dwEndFrame-3 )
            {
                if ( (m_bCurrMtn == _MT_SPELL1) && m_bUseEffect )
                {
                    if ( m_dwCurrEffectFrame - m_dwFstEffectFrame < m_bEffectFrameCnt-5 )
                    {
                        m_dwCurrFrame = m_dwEndFrame - 4;
                    }
                }
            }
            
            if ( m_dwCurrFrame >= m_dwEndFrame )
            {
                m_dwCurrEffectFrame = 0;
                m_dwFstEffectFrame = 0;
                m_dwEndEffectFrame = 0;
                m_bEffectFrame = 0;
                m_bEffectFrameCnt = _DEFAULT_SPELLFRAME;
                m_bUseEffect = false;
                m_bUseSwordEffect = false;
                m_dwCurrFrame = m_dwFstFrame;
                
                if ( m_stFeatureEx.Horse == _HORSE_NONE )
                {
                    if ( m_bWarMode )
                        SetMotionFrame(_MT_ATTACKMODE, m_bCurrDir);
                    else
                        SetMotionFrame(_MT_STAND, m_bCurrDir);
                }
                else
                {
                    SetMotionFrame(_MT_HORSESTAND, m_bCurrDir);
                }
            }
            
        }
    }

    public override bool UpdateMove(bool bIsMoveTime)
    {
        if ( m_bCurrMtn == _MT_WALK || m_bCurrMtn == _MT_RUN || m_bCurrMtn == _MT_HORSEWALK || m_bCurrMtn == _MT_HORSERUN || m_bCurrMtn == _MT_MOODEPO || m_bCurrMtn == _MT_PUSHBACK )
        {
            m_wCurrDelay = 0;
            
            if ( bIsMoveTime )
            {
                if ( m_bCurrMtn == _MT_PUSHBACK )
                {
                    m_bBackStepFrame += 2;
                    if ( m_bBackStepFrame >= m_bBackStepFrameCnt )
                    {
                        m_dwCurrFrame++;
                        m_bBackStepFrame = m_bBackStepFrameCnt;
                    }
                }
                else if ( m_bCurrMtn == _MT_MOODEPO )
                {
                    m_dwCurrFrame+=2;
                    if ( m_dwCurrFrame >= m_dwEndFrame )        m_dwCurrFrame = m_dwEndFrame;
                }
                else
                {
                    m_dwCurrFrame++;
                    PlayMoveSound();
                    if ( m_bMsgHurryCheck )         
                    {
                        m_dwCurrFrame++;
                    }
                }
                
                if ( m_bCurrMtn == _MT_PUSHBACK )
                {
                    if ( m_bBackStepFrame >= m_bBackStepFrameCnt-m_bMoveNextFrmCnt && !m_bIsMoved )
                    {
                        SetMoved();
                        m_bIsMoved = true;
                    }
                }
                else
                {
                    if ( m_dwCurrFrame >= m_dwEndFrame-m_bMoveNextFrmCnt && !m_bIsMoved )
                    {
                        SetMoved();
                        m_bIsMoved = true;
                    }
                }
            }
            
            // ¿¬¼ÓÀûÀÎ ÇÁ·¹ÀÓ Áß¿¡¼­ ÇØ¾ßÇÒÀÏ.
            if ( m_dwCurrFrame >= m_dwEndFrame )
            {
                m_dwCurrFrame = m_dwEndFrame - 1;
                
                m_shShiftTileX = 0;
                m_shShiftTileY = 0;
                m_shShiftPixelX = 0;
                m_shShiftPixelY = 0;
                
                m_dwCurrEffectFrame = 0;
                m_dwFstEffectFrame = 0;
                m_dwEndEffectFrame = 0;
                m_bEffectFrame = 0;
                m_bEffectFrameCnt = _DEFAULT_SPELLFRAME;
                m_bUseEffect = false;
                m_bUseSwordEffect = false;
                m_dwCurrFrame = m_dwFstFrame;
                
                m_bBackStepFrame = 0;
                m_bBackStepFrameCnt = 0;
                
                m_bIsMoved = false;
                
                if ( m_stFeatureEx.Horse == _HORSE_NONE )
                {
                    if ( m_bWarMode )
                        SetMotionFrame(_MT_ATTACKMODE, m_bCurrDir);
                    else
                        SetMotionFrame(_MT_STAND, m_bCurrDir);
                }
                else
                {
                    SetMotionFrame(_MT_HORSESTAND, m_bCurrDir);
                }
            }
            else if ( m_dwCurrFrame < m_dwEndFrame )
            {
                if ( m_bCurrMtn != _MT_PUSHBACK )
                    SetMoving();
                else
                    SetBackStepMoving();
            }
            return true;
        }
        return false;
    }

    public void OnRun(PPacketMsg packet)
    {
        ubyte bDir;
        CharDesc stCharDesc;
        Feature stFeature;
        //  FEATUREEX   stFeatureEx;
        //  m_wPosX = packet.stDefMsg.wParam;
        //  m_wPosY = packet.stDefMsg.wTag;
        bDir         = LoByte(packet.stDefMsg.Series);
        m_bLightSize = HiByte(packet.stDefMsg.Series);

        DecodeBuffer(packet.szEncodeData, &stCharDesc, stCharDesc.sizeof);
        //  fnDecode6BitBuf(&packet.szEncodeData[_CHARDESCSIZE], (char*)&stFeatureEx, sizeof(FEATUREEX));
        
        memcpy(&stFeature, &stCharDesc.Feature, stFeature.sizeof);
        m_nState = stCharDesc.Status;
        
        if ( m_nState & 0X2 ) m_bOpenHealth = true;
        else m_bOpenHealth = false;
        
        ChangeFeature(stFeature, m_stFeatureEx);
        
        if ( m_stFeatureEx.Horse == _HORSE_NONE )  
        {
            SetMotionFrame(_MT_RUN, bDir);
            m_bMoveSpeed = _SPEED_RUN;
        }
        else
        {
            SetMotionFrame(_MT_HORSERUN, bDir);
            m_bMoveSpeed = _SPEED_HORSERUN;
        }
        
        SetMoving();
    }

    public void OnRush(PPacketMsg packet)
    {
        ubyte bDir = cast(ubyte)packet.stDefMsg.Series;
        m_wPosX = packet.stDefMsg.Param;
        m_wPosY = packet.stDefMsg.Tag;
        SetMotionFrame(_MT_MOODEPO, bDir);
        m_bMoveSpeed = _SPEED_WALK;
        SetMoving();
    }

    public void OnMagicFire(PPacketMsg packet)
    {
        IntPoint ptPos;
        ubyte bMagicID;
        int nMagicTargetID, nPos;
        
        ptPos.setX( packet.stDefMsg.Param );
        ptPos.setY( packet.stDefMsg.Tag );
        bMagicID = cast(ubyte)packet.stDefMsg.Series;

        DecodeBuffer(packet.szEncodeData, &nMagicTargetID, nMagicTargetID.sizeof);
        
        if ( bMagicID == _SKILL_SKELLETON )
        {
            return;
        }
        
        if ( bMagicID == _SKILL_SHOOTLIGHTEN )
        {
            ElecMagic pxElecMagic;
            pxElecMagic = new ElecMagic;
            pxElecMagic.CreateMagic(bMagicID, m_wPosX, m_wPosY, ptPos.getX(), ptPos.getY(), null, nMagicTargetID);
            PlayScene.MagicList.add(pxElecMagic);
        }
        //  if ( bMagicID == _SKILL_SHOOTLIGHTEN || bMagicID == _SKILL_FIRE )
        else if ( bMagicID == _SKILL_FIRE )
        {
            ubyte bDir16 = PlayScene.m_xMap.CalcDirection8(m_wPosX, m_wPosY, ptPos.getX(), ptPos.getY());
            IntPoint ptTileGap = IntPoint(0, 0);
            IntPoint ptTileDest = IntPoint(0, 0);
            int bLoopCnt = 0;
            ubyte bDelay = 0;
            
            if ( bMagicID == _SKILL_SHOOTLIGHTEN ) { bLoopCnt = 15; bDelay = 70; }
            else { bLoopCnt = 5; bDelay = 150; }
            
            switch ( bDir16 )
            {
                case 0: ptTileGap.set(0, -1); break;
                case 1: ptTileGap.set(1, -1); break;
                case 2: ptTileGap.set(1, 0); break;
                case 3: ptTileGap.set(1, 1); break;
                case 4: ptTileGap.set(0, 1); break;
                case 5: ptTileGap.set(-1, 1); break;
                case 6: ptTileGap.set(-1, 0); break;
                case 7: ptTileGap.set(-1, -1); break;
                default: break;
            }
            
            for ( int nCnt = 0; nCnt < bLoopCnt; nCnt++ )
            {
                MagicStream pxMagicStream;
                pxMagicStream = new MagicStream;
                ptTileDest.setX( m_wPosX + ptTileGap.getX()*(nCnt+1) );
                ptTileDest.setY( m_wPosY + ptTileGap.getY()*(nCnt+1) );
                
                pxMagicStream.CreateMagic(bMagicID, m_wPosX, m_wPosY, ptTileDest.getX(), ptTileDest.getY(), null, nMagicTargetID, cast(ushort)(nCnt*bDelay), ptTileGap);
                
                if ( bMagicID == _SKILL_SHOOTLIGHTEN )
                    PlayScene.MagicList.add(pxMagicStream);
                else
                    PlayScene.GroundMagicList.add(pxMagicStream);
            }
        }
        else if ( bMagicID == _SKILL_SHIELD )
        {
            Magic pxMagic;
            pxMagic = new Magic;
            pxMagic.CreateMagic(bMagicID, m_wPosX, m_wPosY, m_wPosX, m_wPosY, null, m_dwIdentity);
            PlayScene.MagicList.add(pxMagic);
        }
        else if ( bMagicID != _SKILL_EARTHFIRE &&  bMagicID != _SKILL_HOLYSHIELD )
        {
            Magic pxMagic;
            pxMagic = new Magic;
            pxMagic.CreateMagic(bMagicID, m_wPosX, m_wPosY, ptPos.getX(), ptPos.getY(), null, nMagicTargetID);
            PlayScene.MagicList.add(pxMagic);                      
        }
        
        m_shCurrMagicID = bMagicID;
    }

    public void OnSpell(PPacketMsg packet)
    {
        ubyte bMagicDir; 
        int nMagicID;
        IntPoint ptTargetPos;
        
        ptTargetPos.setX( packet.stDefMsg.Param );
        ptTargetPos.setY( packet.stDefMsg.Tag );
        //  bDir          = packet.stDefMsg.wSeries;
        //  nPos          = fnDecode6BitBuf(packet.szEncodeData, szDecodeMsg, sizeof(szDecodeMsg));
        //  szDecodeMsg[nPos] = '\0';
        DecodeBuffer(packet.szEncodeData, &nMagicID, nMagicID.sizeof);
        
        bMagicDir = PlayScene.m_xMap.CalcDirection8(m_wPosX, m_wPosY, ptTargetPos.getX(), ptTargetPos.getY());
        
        if ( nMagicID == _SKILL_FIREBALL     || nMagicID == _SKILL_FIREBALL2 || nMagicID == _SKILL_FIRE      || nMagicID == _SKILL_SHOOTLIGHTEN ||
            nMagicID == _SKILL_HANGMAJINBUB || nMagicID== _SKILL_DEJIWONHO  || nMagicID == _SKILL_FIRECHARM || nMagicID == _SKILL_BIGCLOAK || nMagicID == _SKILL_SINSU )
            LoadEffect(PlayScene.m_xImage, nMagicID, bMagicDir);
        else
            LoadEffect(PlayScene.m_xImage, nMagicID);
        
        if  ( nMagicID == _SKILL_SHOWHP )               m_bEffectFrameCnt = 20;
        else if ( nMagicID == _SKILL_LIGHTFLOWER )      m_bEffectFrameCnt = 20;
        else if ( nMagicID == _SKILL_SPACEMOVE )        m_bEffectFrameCnt = 19;
        else if ( nMagicID == _SKILL_LIGHTENING )       m_bEffectFrameCnt = 17;
        
        m_bWarMode = m_bUseEffect = true;
        
        /* if ( nMagicID == _SKILL_SHIELD )
        {
            m_dwFstEffectFrame = 50;
            m_dwEndEffectFrame = 60;
            m_dwCurrEffectFrame = 50;
            m_bEffectFrame = 0;
            m_bEffectFrameCnt = _DEFAULT_SPELLFRAME;
        }*/

        m_shCurrMagicID = nMagicID;
        
        switch( nMagicID )
        {
            case _SKILL_HANGMAJINBUB:
            case _SKILL_DEJIWONHO:
            case _SKILL_FIRECHARM:
            case _SKILL_FIRE:
            case _SKILL_FIREBALL2:
            case _SKILL_SINSU:
            case _SKILL_FIREBALL:
            case _SKILL_SHOOTLIGHTEN:
            case _SKILL_BIGCLOAK:
                SetMotionFrame(_MT_SPELL1, bMagicDir);
                break;
            case _SKILL_FIREWIND:   
            case _SKILL_AMYOUNSUL:
            case _SKILL_TAMMING:
            case _SKILL_KILLUNDEAD:
            case _SKILL_HEALLING:
            case _SKILL_HOLYSHIELD:
            case _SKILL_BIGHEALLING:
            case _SKILL_LIGHTFLOWER:
            case _SKILL_SKELLETON:
            case _SKILL_SNOWWIND:
            case _SKILL_SHIELD:
            case _SKILL_SHOWHP:
            case _SKILL_EARTHFIRE:
            case _SKILL_FIREBOOM:
            case _SKILL_SPACEMOVE:
            case _SKILL_CLOAK:
            case _SKILL_LIGHTENING:
            default:
                SetMotionFrame(_MT_SPELL2, bMagicDir);
                break;          
        }
    }

    public override void OnCharDescPacket(PPacketMsg packet)
    {
        CharDesc stCharDesc;
        Feature stFeature;
        
        DecodeBuffer(packet.szEncodeData, &stCharDesc, stCharDesc.sizeof);
        
        memcpy(&stFeature, &stCharDesc.Feature, stFeature.sizeof);
        m_nState = stCharDesc.Status;
        
        if ( m_nState & 0X2 ) m_bOpenHealth = true;
        else m_bOpenHealth = false;
        
        ChangeFeature(stFeature, m_stFeatureEx);
    }

    public override void OnFeatureChanged(PPacketMsg packet)
    {
        Feature stFeature;
        int nFeature = MakeLong(packet.stDefMsg.Param, packet.stDefMsg.Tag);
        
        memcpy(&stFeature, &nFeature, nFeature.sizeof);
        
        ChangeFeature(stFeature, m_stFeatureEx);
    }

    public void OnCharStatusChanged(PPacketMsg packet)
    {
        m_shHitSpeed = packet.stDefMsg.Series;
        m_nState = MakeLong(packet.stDefMsg.Param, packet.stDefMsg.Tag);
        
        if( m_nState & 0X2 ) m_bOpenHealth = true;
        else m_bOpenHealth = false;
    }

    public override void OnBackStep(PPacketMsg packet)
    {
        ubyte bDir = cast(ubyte)packet.stDefMsg.Series;
        m_wPosX = packet.stDefMsg.Param;
        m_wPosY = packet.stDefMsg.Tag;
        SetMotionFrame(_MT_PUSHBACK, bDir);
        if ( m_bCurrDir < 4 ) m_bMoveDir = cast(ubyte)(m_bCurrDir + 4);
        else m_bMoveDir = cast(ubyte)(m_bCurrDir - 4);
        m_bBackStepFrame    = 0;
        m_bBackStepFrameCnt = 6;
        m_bMoveSpeed = _SPEED_WALK;
        SetBackStepMoving();
    }

    public override void OnDeath(PPacketMsg packet)
    {
        ubyte bDir;
        m_wPosX = packet.stDefMsg.Param;
        m_wPosY = packet.stDefMsg.Tag;
        bDir = cast(ubyte)packet.stDefMsg.Series;
        OnCharDescPacket(packet);
        SetMotionFrame(_MT_DIE, bDir);
        if ( packet.stDefMsg.Ident == SM_DEATH ) 
        {
            m_dwCurrFrame = m_dwEndFrame - 1;
            m_bIsDead = true;
        }
    }

    public override void OnWalk(PPacketMsg packet)
    {
        ubyte bDir;
        CharDesc stCharDesc;
        Feature stFeature;
        //  FEATUREEX   stFeatureEx;
        //  m_wPosX = packet.stDefMsg.Param;
        //  m_wPosY = packet.stDefMsg.Tag;
        bDir = LoByte(packet.stDefMsg.Series);
        m_bLightSize = HiByte(packet.stDefMsg.Series);

        DecodeBuffer(packet.szEncodeData, &stCharDesc, stCharDesc.sizeof);
        //  fnDecode6BitBuf(&packet.szEncodeData[_FEATURESIZE*2], (char*)&stFeatureEx, sizeof(FEATUREEX));
        
        memcpy(&stFeature, &stCharDesc.Feature, stFeature.sizeof);
        m_nState = stCharDesc.Status;
        
        if( m_nState & 0X2 ) m_bOpenHealth = true;
        else m_bOpenHealth = false;
        
        ChangeFeature(stFeature, m_stFeatureEx);
        
        if ( m_stFeatureEx.Horse == _HORSE_NONE ) SetMotionFrame(_MT_WALK, bDir);
        else SetMotionFrame(_MT_HORSEWALK, bDir);
        
        m_bMoveSpeed = _SPEED_WALK;
        SetMoving();
    }

    public override void OnTurn(PPacketMsg packet)
    {
        ubyte bDir;
        CharDesc stCharDesc;
        Feature stFeature;
        int nPos;
        m_wPosX = packet.stDefMsg.Param;
        m_wPosY = packet.stDefMsg.Tag;
        bDir = LoByte(packet.stDefMsg.Series);
        m_bLightSize = HiByte(packet.stDefMsg.Series);

        DecodeBuffer(packet.szEncodeData, &stCharDesc, stCharDesc.sizeof);
        // TODO:
        /*DecodeBuffer(packet.szEncodeData[_CHARDESCSIZE], &m_stFeatureEx, m_stFeatureEx.sizeof);
        
        if (strlen(packet.szEncodeData) > _CHARDESCSIZE)
        {
            nPos = fnDecode6BitBuf(&packet.szEncodeData[_CHARDESCSIZE +_FEATURESIZEEX], m_szName, sizeof(m_szName));
            m_szName[nPos] = '\0';
            
            CHAR* szClr = strrchr(m_szName, '/');
            
            if ( szClr )
            {
                m_dwNameClr = GetUserNameColor(atoi(szClr+1));
                *szClr = '\0';
            }
        }*/
        
        memcpy(&stFeature, &stCharDesc.Feature, stFeature.sizeof);
        m_nState = stCharDesc.Status;
        
        if( m_nState & 0X2 ) m_bOpenHealth = true;
        else m_bOpenHealth = false;
        
        if ( !m_bIsMon )
        {
            ChangeFeature(stFeature, m_stFeatureEx);
            
            if ( m_stFeatureEx.Horse == _HORSE_NONE ) SetMotionFrame(_MT_STAND, bDir);
            else SetMotionFrame(_MT_HORSESTAND, bDir);
        }
        else
        {
            SetMotionFrame(_MT_MON_STAND, bDir);
        }
    }

    public override void OnStruck(PPacketMsg packet)
    {
        MessageBodyWL stMsgBodyWl;
        Feature stFeature;

        ushort wDamage = packet.stDefMsg.Series;
        m_wHP = packet.stDefMsg.Param;
        m_wMAXHP = packet.stDefMsg.Tag;
        m_bHPPercent = cast(ubyte)get_percentage(packet.stDefMsg.Param, packet.stDefMsg.Tag);
        m_bShieldCurrFrm = 0;

        DecodeBuffer(packet.szEncodeData, &stMsgBodyWl, stMsgBodyWl.sizeof);
        memcpy(&stFeature, &stMsgBodyWl.Param1, stFeature.sizeof);
        
        if ( !m_bIsMon )
        {
            ChangeFeature(stFeature, m_stFeatureEx);
            
            if ( m_stFeatureEx.Horse == _HORSE_NONE ) SetMotionFrame(_MT_HITTED, m_bCurrDir);
            else SetMotionFrame(_MT_HORSEHIT, m_bCurrDir);
        }
        else
        {
            SetMotionFrame(_MT_MON_HITTED, m_bCurrDir);
        }
        
        // ¸ó½ºÅÍÀÏ¶§.
        if ( m_stHitter.Gender == _GENDER_MON )
        {
            Magic pxMagic;
            switch ( m_stHitter.Dress )
            {
                case 2:     // ÄÉÆÈ·ÎÇÁ.
                    pxMagic = new Magic;
                    pxMagic.CreateMagic(_MONMAGIC_KEPAL, m_wPosX, m_wPosY, m_wPosX, m_wPosY, this, 0);
                    PlayScene.MagicList.add(pxMagic);
                    break;
                case 8:     // °©ÁÖ°³¹Ì.
                    pxMagic = new Magic;
                    pxMagic.CreateMagic(_MONMAGIC_GREATANT, m_wPosX, m_wPosY, m_wPosX, m_wPosY, this);
                    PlayScene.MagicList.add(pxMagic);
                    break;
                case 14:    // º´¿ë°³¹Ì.
                    pxMagic = new Magic;
                    pxMagic.CreateMagic(_MONMAGIC_ANT, m_wPosX, m_wPosY, m_wPosX, m_wPosY, this);
                    PlayScene.MagicList.add(pxMagic);
                    break;
                case 16:    // µå³­°³¹Ì.
                    pxMagic = new Magic;
                    pxMagic.CreateMagic(_MONMAGIC_WORKANT, m_wPosX, m_wPosY, m_wPosX, m_wPosY, this);
                    PlayScene.MagicList.add(pxMagic);
                    break;
                case 67:    // ÃË·æ½Å.
                    pxMagic = new Magic;
                    pxMagic.CreateMagic(_MONMAGIC_BIGGINE_CHAR, m_wPosX, m_wPosY, m_wPosX, m_wPosY, this);
                    PlayScene.MagicList.add(pxMagic);
                    break;
                default :
                    break;
            }
        }
        m_stHitter = m_stHitter.init;
    }

    public override void OnHit(PPacketMsg packet)
    {
        ubyte bDir;
        ushort hitmode = packet.stDefMsg.Ident;
        bDir = cast(ubyte)packet.stDefMsg.Series;
        m_wPosX = packet.stDefMsg.Param;
        m_wPosY = packet.stDefMsg.Tag;
        
        if ( !m_bIsMon )
        {
            m_dwCurrEffectFrame = 50;
            m_dwFstEffectFrame = 50;
            m_dwEndEffectFrame = 60;
            m_bEffectFrame = 0;
            m_bEffectFrameCnt = _DEFAULT_SPELLFRAME;
            m_bWarMode = true;
            
            if ( hitmode == SM_WIDEHIT )
            {
                SetMotionFrame(_MT_ONEHSWING, bDir);
                LoadEffect(PlayScene.m_xImage, _SKILL_BANWOL, bDir);
                m_bUseSwordEffect = true;
            }
            else if ( hitmode == SM_POWERHIT )
            {
                SetMotionFrame(_MT_ONEVSWING, bDir);
                LoadEffect(PlayScene.m_xImage, _SKILL_YEDO, bDir);
                m_bUseSwordEffect = true;
            }
            else if ( hitmode == SM_FIREHIT )
            {
                SetMotionFrame(_MT_ONEVSWING, bDir);
                LoadEffect(PlayScene.m_xImage, _SKILL_FIRESWORD, bDir);
                m_bUseSwordEffect = true;
            }
            else if ( hitmode == SM_LONGHIT )
            {
                SetMotionFrame(_MT_ONEVSWING, bDir);
                LoadEffect(PlayScene.m_xImage, _SKILL_ERGUM, bDir);
                m_bUseSwordEffect = true;
            }
            else
            {
                bDir = LoByte(packet.stDefMsg.Series);
                ubyte bAttackMode = HiByte(packet.stDefMsg.Series);
                SetMotionFrame(_MT_ONEVSWING, bDir);
            }
        }
    }

    public void OnButch(PPacketMsg packet)
    {
        ubyte bDir;
        m_wPosX = packet.stDefMsg.Param;
        m_wPosY = packet.stDefMsg.Tag;
        bDir = cast(ubyte)packet.stDefMsg.Series;
        SetMotionFrame(_MT_CUT, bDir);
    }

    public override bool DrawActor(MirMap pxMap, bool bFocused = false, bool bShadowAblended = false, 
                           bool bUseScrnPos = true, bool bDrawShadow = true)
    {
        // ÁÂÇ¥Ã³¸®.
        if ( bUseScrnPos )
        {
            m_shScrnPosX = (m_wPosX - pxMap.m_shStartViewTileX) * _CELL_WIDTH + _VIEW_CELL_X_START - pxMap.m_shViewOffsetX + m_shShiftPixelX;
            m_shScrnPosY = (m_wPosY - pxMap.m_shStartViewTileY) * _CELL_HEIGHT+ _VIEW_CELL_Y_START - pxMap.m_shViewOffsetY + m_shShiftPixelY;
        }
        
        ALLEGRO_BITMAP* horse_bmp, weapon_bmp, actor_bmp, hair_bmp;
        //int bmp_width, bmp_height;
        
        ImageInfo horse_imgi, weapon_imgi, actor_imgi, hair_imgi;
        ubyte bShadowType;
        short shPX, shPY, shShadowPX, shShadowPY;
        

        //---------------------------------------------------------------------------------------------------------------//
        // °¢ ÀÌ¹ÌÁö ¼¼ÆÃ¹× ÁÂÇ¥¿µ¿ª Ã¼Å©.
        //---------------------------------------------------------------------------------------------------------------//
        // ¸».
        if ( m_stFeatureEx.Horse != _HORSE_NONE && m_pxHorseImage !is null )
        {
            if ( m_bCurrMtn >= _START_HORSE_MTN )
            {       
                m_dwCurrHorseFrame = (m_stFeatureEx.Horse*_MAX_HORSE_FRAME-_MAX_HORSE_FRAME)+(m_dwCurrFrame-(_MAX_HERO_FRAME*m_stFeature.Dress)-_START_HORSE_FRAME);
                horse_bmp = m_pxHorseImage.GetCachedImageEx(m_dwCurrHorseFrame, horse_imgi);
                if ( horse_bmp is null )
                    return false;

                shPX = horse_imgi.PosX;
                shPY = horse_imgi.PosY;
                m_rcHorse.set(m_shScrnPosX + shPX, m_shScrnPosY + shPY,
                              m_shScrnPosX + shPX + horse_imgi.Width, 
                              m_shScrnPosX + shPY + horse_imgi.Height);
            }
        } 
        
        // ¹«±â.
        if ( m_stFeature.Weapon != _WEAPON_NONE && m_pxWeaponImage !is null )
        {
            if ( m_bCurrMtn < _MAX_WEAPON_MTN )
            {   
                m_dwCurrWeaponFrame = (((m_stFeature.Weapon-1)%10)*_MAX_WEAPON_FRAME)+(m_dwCurrFrame-(_MAX_HERO_FRAME*m_stFeature.Dress));
                weapon_bmp = m_pxWeaponImage.GetCachedImageEx(m_dwCurrWeaponFrame, weapon_imgi);
                if ( weapon_bmp is null )
                    return false;

                shPX = weapon_imgi.PosX;
                shPY = weapon_imgi.PosY;
                m_rcWeapon.set(m_shScrnPosX + shPX, m_shScrnPosY + shPY, 
                               m_shScrnPosX + shPX + weapon_imgi.Width, 
                               m_shScrnPosY + shPY + weapon_imgi.Height);
            }
        }
        
        // Ä³¸¯ÅÍ.
        if ( m_pxActorImage !is null )
        {
            actor_bmp = m_pxActorImage.GetCachedImageEx(m_dwCurrFrame, actor_imgi);
            if ( actor_bmp is null )
                return false;

            shPX = actor_imgi.PosX;
            shPY = actor_imgi.PosY;
            bShadowType = actor_imgi.Shadow;
            shShadowPX  = actor_imgi.ShadowPX;
            shShadowPY  = actor_imgi.ShadowPY;
            
            m_rcActor.set(m_shScrnPosX + shPX, m_shScrnPosY + shPY, 
                          m_shScrnPosX + shPX + actor_imgi.Width, 
                          m_shScrnPosY + shPY + actor_imgi.Height);

            assignTargetRegion(m_rcActor, m_rcTargetRgn);
        }
        
        // ¸Ó¸®.
        if ( m_stFeature.Hair != _HAIR_NONE && m_pxHairImage !is null )
        {
            m_dwCurrHairFrame = (m_stFeature.Hair*_MAX_HERO_FRAME-_MAX_HERO_FRAME)+(m_dwCurrFrame-(_MAX_HERO_FRAME*m_stFeature.Dress));
            hair_bmp = m_pxHairImage.GetCachedImageEx(m_dwCurrHairFrame, hair_imgi);
            if ( hair_bmp is null )
                return false;

            shPX = hair_imgi.PosX;
            shPY = hair_imgi.PosY;
            m_rcHair.set(m_shScrnPosX + shPX, m_shScrnPosY + shPY, 
                         m_shScrnPosX + shPX + hair_imgi.Width, 
                         m_shScrnPosY + shPY + hair_imgi.Height);
        }
        
        //---------------------------------------------------------------------------------------------------------------//
        // ±×¸²ÀÚ±×¸®±â.
        //---------------------------------------------------------------------------------------------------------------//
        ubyte bOpaRate = 70;
        ushort wState = GetCharState();
        
        // ¹«±â.
        /+if ( m_stFeature.Weapon != _WEAPON_NONE && m_pxWeaponImage !is null )
        {
            if ( m_bCurrMtn < _MAX_WEAPON_MTN )
            {
                if ( bDrawShadow )
                {
                    if ( !m_bIsDead )
                    {
                        // TODO:
                        /*g_xMainWnd.DrawWithShadowABlend(
                            m_rcWeapon.getLeft(), m_rcWeapon.getTop(),
                            m_pxWeaponImage.m_lpstNewCurrWilImageInfo.shWidth,
                            m_pxWeaponImage.m_lpstNewCurrWilImageInfo.shHeight,
                            m_pxWeaponImage.m_lpstNewCurrWilImageInfo.shPX,
                            m_pxWeaponImage.m_lpstNewCurrWilImageInfo.shPY,
                            (WORD*)m_pxWeaponImage.m_pbCurrImage,                      
                            _CLIP_WIDTH, _CLIP_HEIGHT, g_xGameProc.m_wShadowClr, bShadowAblended, bOpaRate);*/
                    }
                    /*              else
                {
                    g_xMainWnd.DrawWithShadowABlend(
                                    m_rcWeapon.getLeft()+3, m_rcWeapon.getTop()-2,
                                    m_pxWeaponImage.m_lpstNewCurrWilImageInfo.shWidth,
                                    m_pxWeaponImage.m_lpstNewCurrWilImageInfo.shHeight,
                                    m_pxWeaponImage.m_lpstNewCurrWilImageInfo.shPX,
                                    m_pxWeaponImage.m_lpstNewCurrWilImageInfo.shPY,
                                    (WORD*)m_pxWeaponImage.m_pbCurrImage,                      
                                    _CLIP_WIDTH, _CLIP_HEIGHT, g_xGameProc.m_wShadowClr, bShadowAblended, bOpaRate);
                }
*/          }
            }
        }
        
        // ¸».
        if ( m_stFeatureEx.Horse != _HORSE_NONE && m_pxHorseImage !is null )
        {
            if ( m_bCurrMtn >= _START_HORSE_MTN )
            {   
                if ( bDrawShadow )
                {
                    if ( !m_bIsDead )
                    {
                        // TODO:
                        /*g_xMainWnd.DrawWithShadowABlend(
                            m_rcHorse.getLeft(), m_rcHorse.getTop(),
                            m_pxHorseImage.m_lpstNewCurrWilImageInfo.shWidth,
                            m_pxHorseImage.m_lpstNewCurrWilImageInfo.shHeight,
                            m_pxHorseImage.m_lpstNewCurrWilImageInfo.shPX,
                            m_pxHorseImage.m_lpstNewCurrWilImageInfo.shPY,
                            (WORD*)m_pxHorseImage.m_pbCurrImage,                       
                            _CLIP_WIDTH, _CLIP_HEIGHT, g_xGameProc.m_wShadowClr, bShadowAblended, bOpaRate);*/
                    }
                    else
                    {
                        // TODO:
                        /*g_xMainWnd.DrawWithShadowABlend(
                            m_rcHorse.getLeft()+3, m_rcHorse.getTop()+2,
                            m_pxHorseImage.m_lpstNewCurrWilImageInfo.shWidth,
                            m_pxHorseImage.m_lpstNewCurrWilImageInfo.shHeight,
                            m_pxHorseImage.m_lpstNewCurrWilImageInfo.shPX,
                            m_pxHorseImage.m_lpstNewCurrWilImageInfo.shPY,
                            (WORD*)m_pxHorseImage.m_pbCurrImage,                       
                            _CLIP_WIDTH, _CLIP_HEIGHT, g_xGameProc.m_wShadowClr, bShadowAblended, bOpaRate);*/
                    }
                }
            }
        }
        
        // Ä³¸¯ÅÍ.
        if ( m_pxActorImage )
        {
            INT nStartX1 = m_shScrnPosX + shShadowPX;
            INT nStartY1 = m_shScrnPosY + shShadowPY;
            
            if ( bDrawShadow )
            {
                if ( !m_bIsDead )
                {
                    // TODO:
                    /*g_xMainWnd.DrawWithShadowABlend(
                        nStartX1, nStartY1,
                        m_pxActorImage.m_lpstNewCurrWilImageInfo.shWidth,
                        m_pxActorImage.m_lpstNewCurrWilImageInfo.shHeight,
                        (WORD*)m_pxActorImage.m_pbCurrImage,
                        _CLIP_WIDTH, _CLIP_HEIGHT, g_xGameProc.m_wShadowClr, bShadowAblended, bShadowType, bOpaRate);*/
                }
                else
                {
                    // TODO:
                    /*g_xMainWnd.DrawWithShadowABlend(
                        m_rcActor.getLeft()+3, m_rcActor.getTop()+2,
                        m_pxActorImage.m_lpstNewCurrWilImageInfo.shWidth,
                        m_pxActorImage.m_lpstNewCurrWilImageInfo.shHeight,
                        (WORD*)m_pxActorImage.m_pbCurrImage,
                        _CLIP_WIDTH, _CLIP_HEIGHT, g_xGameProc.m_wShadowClr, bShadowAblended, 50, bOpaRate);*/
                }
            }
        }+/
        
        // ¸Ó¸®´Â ±×¸²ÀÚ¸¦ ±×¸®Áö ¾Ê´Â´Ù.
        if ( m_wABlendDelay )       
        {
            bShadowAblended = true;
            
            if ( m_wABlendDelay )   
            {
                if ( !m_bABlendRev )
                    bOpaRate = cast(ubyte)( 100 - ( (m_wABlendCurrDelay * 100) / m_wABlendDelay ) );
                else
                    bOpaRate = cast(ubyte)( (m_wABlendCurrDelay * 100) / m_wABlendDelay );
            }
            
            if ( bFocused )                             
            {
                if ( !m_bABlendRev )
                    bOpaRate -= 20;
                else
                    bOpaRate += 20;
            }
            
            if ( bOpaRate < 0 && bOpaRate > 100 ) bOpaRate = 0;
        }
        else if ( wState == _STATE_ABLEND ) bShadowAblended = true;
        
        //---------------------------------------------------------------------------------------------------------------//
        // ÀÌ¹ÌÁö ±×¸®±â.
        //---------------------------------------------------------------------------------------------------------------//
        // ¸».
        if ( m_stFeatureEx.Horse != _HORSE_NONE && m_pxHorseImage !is null )
        {
            if ( m_bCurrMtn >= _START_HORSE_MTN )
            {
                DrawWithEffected(m_rcHorse.getLeft(), m_rcHorse.getTop(), horse_imgi.Width, horse_imgi.Height, horse_bmp, 
                                 0XFFFF, 0XFFFF, bFocused, bOpaRate, wState);
            }
        }
        
        // ¹«±â¸¦ ¸ÕÀú ±×·Á¾ßµÉ¹æÇâ.
        if ( m_stFeature.Weapon != _WEAPON_NONE && m_pxWeaponImage !is null && SpriteInfo.m_bWOrder[(m_dwCurrFrame-(_MAX_HERO_FRAME*m_stFeature.Dress))] )
        {
            if ( m_bCurrMtn < _MAX_WEAPON_MTN )
            {
                DrawWithEffected(m_rcWeapon.getLeft(), m_rcWeapon.getTop(), weapon_imgi.Width, weapon_imgi.Height, weapon_bmp,
                                 0XFFFF, 0XFFFF, bFocused, bOpaRate, wState);
            }
        }
        
        // Ä³¸¯ÅÍ.
        if ( m_pxActorImage !is null )
        {
            DrawWithEffected(m_rcActor.getLeft(), m_rcActor.getTop(), actor_imgi.Width, actor_imgi.Height, actor_bmp,
                             m_stFeatureEx.DressColor, m_stFeatureEx.HairColor, bFocused, bOpaRate, wState);
        }
        
        // ¸Ó¸®.
        if ( m_stFeature.Hair != _HAIR_NONE && m_pxHairImage !is null )
        {
            DrawWithEffected(m_rcHair.getLeft(), m_rcHair.getTop(), hair_imgi.Width, hair_imgi.Height, hair_bmp, 
                             m_stFeatureEx.DressColor, m_stFeatureEx.HairColor, bFocused, bOpaRate, wState);
        }           
        
        // ¹«±â¸¦ ³ªÁß¿¡ ±×·Á¾ßµÉ ¹æÇâ.
        if ( m_stFeature.Weapon != _WEAPON_NONE && m_pxWeaponImage !is null && !SpriteInfo.m_bWOrder[(m_dwCurrFrame-(_MAX_HERO_FRAME*m_stFeature.Dress))] ) 
        {
            if ( m_bCurrMtn < _MAX_WEAPON_MTN )
            {
                DrawWithEffected(m_rcWeapon.getLeft(), m_rcWeapon.getTop(), weapon_imgi.Width, weapon_imgi.Height, weapon_bmp,
                                 0XFFFF, 0XFFFF, bFocused, /*bOpaRate*/20, wState);
            }
        }

        DrawEffect();
        ShowShield();
        
        if ( !bDrawShadow )
            return true;
        
        if ( bUseScrnPos )
        {
            if ( m_bLightSize == 2 )
                PlayScene.m_xLightFog.SetLightRadiusWithCircle(m_rcActor.getLeft()+_CELL_WIDTH/2, m_rcActor.getTop()+_CELL_HEIGHT/2, 3, 100, 100, 100);
            else
                PlayScene.m_xLightFog.SetLightRadiusWithCircle(m_rcActor.getLeft()+_CELL_WIDTH/2, m_rcActor.getTop()+_CELL_HEIGHT/2, m_bLightSize, 255, 255, 255);
        }
        else
        {
            if ( m_bLightSize == 2 )
                PlayScene.m_xLightFog.SetLightRadiusWithCircle(400, 242, 3, 100, 100, 100);
            else
                PlayScene.m_xLightFog.SetLightRadiusWithCircle(400, 242, m_bLightSize, 255, 255, 255);
        }
        return true;
    }
}



public struct SubAbility
{
    public ushort AntiMagic;       // ¸¶¹ýÈ¸ÇÇ.
    public ubyte HitPoint;         // Á¤È®.
    public ubyte SpeedPoint;       // ¹ÎÃ¸.
    public ubyte AntiPoison;       // Áßµ¶È¸ÇÇ.
    public ubyte PoisonRecover;    // Áßµ¶È¸º¹.
    public ubyte HealthRecover;    // Ã¼·ÂÈ¸º¹.
    public ubyte SpellRecover;     // ¸¶·ÂÈ¸º¹.
}

public class MyHero : Hero
{
    //---------------------------------------------------------------------------------------------------------------//
    //  Ä³¸¯ÅÍ º¸Á¤¿ë º¯¼ö.
    public uint m_dwMotionLockTime;
    public bool m_bInputLock;                   // Ä³¸¯ÅÍ°¡ µ¿ÀÛµÇ¸é ¾î¶²ÀÔ·Âµµ ¹ÞÁö ¾Ê´Â´Ù.
    public bool m_bMotionLock;                  // Ä³¸¯ÅÍÀÇ µ¿ÀÛÀÌ ¼¼ÆÃµÇ¸é ¼­¹ö¿¡¼­ µ¿ÀÛ½ÂÀÎÀÌ ¿À±âÀü±îÁø ¾î¶²µ¿ÀÛµµ ¹ÞÁö ¾Ê´Â´Ù.
    //---------------------------------------------------------------------------------------------------------------//
    
    public MirMap m_pxMap;
    public bool m_bCanRun;
    public ubyte m_bAttackMode;
    public Ability m_stAbility;
    public SubAbility m_stSubAbility;
    public ubyte m_bJob;
    public uint m_nGold;
    
    public PacketQueue m_xPriorPacketQueue;     // Actor°¡ Ã³¸®ÇØ¾ßµÉ ¸Þ½ÃÁö¸¦ ÀúÀåÇÏ°í ÀÖ´Â Å¥.
    
    //---------------------------------------------------------------------------------------------------------------//
    //  Ä³¸¯ÅÍ Delay°ü·Ã º¯¼ö.
    public TickDuration m_dwLastHitTime;                // Ä®ÁúÀÌ µé¾î°¬À»¶§ÀÇ Æ½ º¸°ü°ª.
    public TickDuration m_dwLastSpellTime;              // ¸¶¹ýÀÌ µé¾î°¬À»¶§ÀÇ Æ½ º¸°ü°ª.
    public TickDuration m_dwLastMagicTime;              // ¿°È­°á ½ÃÀüÈÄÀÇ Æ½º¸°ü°ª.
    public TickDuration m_dwLastStruckTime;             // ¸Â¾ÒÀ»¶§ÀÇ Æ½ º¸°ü°ª.
    public TickDuration m_dwLastPKStruckTime;
    public TickDuration m_dwLastRushTime;               // ¹«ÅÂº¸ ½ÃÀüÈÄÀÇ Æ½º¸°ü°ª.
    public TickDuration m_dwLastFireHitTime;            // ¿°È­°á ½ÃÀüÈÄÀÇ Æ½º¸°ü°ª.
    
    public int m_wMagicPKDelayTime;                     // ¸¶¹ýÀ¸·Î »ç¶÷À» °ø°ÝÇßÀ»¶§ÀÇ ÆÐ³ÎÆ¼Delay.(°È±â¿Í ¶Ù±â¿¡ Àû¿ëµÈ´Ù.)
    public int m_wMagicDelayTime;                       // ¸¶¹ýÀ»¾²°í ³­ÈÄ ´ÙÀ½¸¶¹ýÀÌ µé¾î°¥¶§±îÁöÀÇ Delay.
    //---------------------------------------------------------------------------------------------------------------//


    public this()
    {
        InitActor();
    }

    public ~this()
    {
        DestroyActor();
    }
    
    public override void InitActor()
    {
        super.InitActor();

        TickDuration curtime = Clock.currSystemTick();
        
        m_wOldPosX = 0;
        m_wOldPosY = 0;
        m_bOldDir = 0;
        m_dwWarModeTime = 0;
        m_dwMotionLockTime = 0;
        
        m_wMagicDelayTime = 0;
        m_wMagicPKDelayTime = 0;
        
        m_bJob = 0;
        m_nGold = 0;
        
        m_dwLastHitTime = curtime;
        m_dwLastSpellTime = curtime;
        m_dwLastMagicTime = curtime;
        m_dwLastStruckTime = curtime;
        m_dwLastPKStruckTime = curtime;
        m_dwLastRushTime = curtime;
        m_dwLastFireHitTime = curtime;
        
        m_bCanRun = false;
        m_bInputLock = false;
        m_bMotionLock = false;
        m_bWarMode = false;
        
        m_pxMap = null;
        
        m_bAttackMode = _MT_ONEHSWING;
        
        m_stAbility = m_stAbility.init;
        m_stSubAbility = m_stSubAbility.init;
    }

    public override void DestroyActor()
    {
        int nCnt, nLeftMsgCnt;
        PPacketMsg packet;

        super.DestroyActor();
        
        packet  = null;
        nLeftMsgCnt = m_xPriorPacketQueue.count();
        
        // ½×¿©ÀÖ´Â ÆÐÅ¶À» Áö¿î´Ù.
        if ( nLeftMsgCnt > 0 )
        {
            for ( nCnt = 0; nCnt < nLeftMsgCnt; nCnt++ )
            {
                packet = m_xPriorPacketQueue.dequeue();
                if ( packet !is null )
                {
                    destroy(packet);
                }
            }
        }
        
        InitActor();
    }
    
    public void SetMotionState(ubyte bMtn, ubyte bDir, int nMouseTargetID = 0, bool bIsDead = false, IntPoint* lpptPos = null, int skill = -1)
    {
        if ( !m_bIsMon )
        {
            switch ( bMtn )
            {
                case _MT_WALK:
                case _MT_HORSEWALK:
                {
                    if ( lpptPos !is null )
                    {
                        // TODO: should be part of PlayScene
                        /*IntPoint ptNext;
                        
                        for ( int nCnt = 0; nCnt < _MAX_DIRECTION; nCnt++ )
                        {
                            m_pxMap.GetNextTileCanMove(lpptPos->x, lpptPos->y, nCnt, 1, &ptNext);
                            if ( !m_pxMap->IsDoorOpen(ptNext.x, ptNext.y) )
                            {
                                g_xClientSocket.SendOpenDoor(ptNext.x, ptNext.y, m_pxMap->GetDoor(ptNext.x, ptNext.y));
                                break;
                            }
                        }*/
                        
                        MySocket.SendActMsg(CM_WALK, lpptPos.getX(), lpptPos.getY(), bDir);
                        m_bMotionLock = m_bInputLock = true;
                        m_wOldPosX = m_wPosX;
                        m_wOldPosY = m_wPosY;
                        m_bOldDir  = m_bCurrDir;
                        
                        SetMotionFrame(bMtn, bDir);
                        m_bMoveSpeed = _SPEED_WALK;
                        
                        m_pxMap.ScrollMap(m_bMoveDir, m_dwCurrFrame-m_dwFstFrame, m_bMoveSpeed);
                    }
                    break;
                }
                case _MT_RUN:
                case _MT_HORSERUN:
                {
                    if ( lpptPos !is null )
                    {
                        IntPoint ptNext;
                        IntPoint ptStart;
                        
                        if ( bMtn == _MT_RUN )
                            m_bMoveSpeed = _SPEED_RUN;
                        else
                            m_bMoveSpeed = _SPEED_HORSERUN;

                        // TODO: should be part of PlayScene
                        /*m_pxMap->GetNextTileCanMove(m_wPosX, m_wPosY, bDir, 1, &ptStart);
                        for ( INT nCnt = 0; nCnt < _MAX_DIRECTION; nCnt++ )
                        {
                            for ( INT nSpeedCnt = 0; nSpeedCnt < m_bMoveSpeed; nSpeedCnt++ )
                            {
                                m_pxMap->GetNextTileCanMove(ptStart.x, ptStart.y, nCnt, nSpeedCnt+1, &ptNext);
                                if ( !m_pxMap->IsDoorOpen(ptNext.x, ptNext.y) )
                                {
                                    g_xClientSocket.SendOpenDoor(ptNext.x, ptNext.y, m_pxMap->GetDoor(ptNext.x, ptNext.y));
                                    break;
                                }
                            }
                        }*/
                        
                        MySocket.SendActMsg(CM_RUN, lpptPos.getX(), lpptPos.getY(), bDir);
                        m_bMotionLock = m_bInputLock = true;
                        m_wOldPosX = m_wPosX;
                        m_wOldPosY = m_wPosY;
                        m_bOldDir  = m_bCurrDir;
                        
                        SetMotionFrame(bMtn, bDir);
                        
                        m_pxMap.ScrollMap(m_bMoveDir, m_dwCurrFrame-m_dwFstFrame, m_bMoveSpeed);           
                    }
                    break;
                }
                case _MT_ONEHSWING:
                {
                    if ( m_bAttackMode == _MT_ONEHSWING || m_bAttackMode == _MT_ONEVSWING )
                    {
                        auto bAttackStyle = random(2);
                        if ( bAttackStyle ) m_bAttackMode = _MT_ONEVSWING;
                        else m_bAttackMode = _MT_ONEHSWING;
                        
                        m_bAttackMode = _MT_ONEVSWING;
                    }           
                    
                    if ( lpptPos !is null && CanNextHit() )
                    {
                        m_bMotionLock   = m_bInputLock = true;
                        m_wOldPosX      = m_wPosX;
                        m_wOldPosY      = m_wPosY;
                        m_bOldDir       = m_bCurrDir;
                        m_bWarMode      = true;
                        m_dwWarModeTime = 0;
                        
                        if ( m_bUseErgum && PlayScene.TargetInLongAttack(bDir) )
                        {
                            SetMotionFrame(_MT_ONEVSWING, bDir);
                            LoadEffect(PlayScene.m_xImage, _SKILL_ERGUM, bDir);
                            m_bUseSwordEffect = true;
                            MySocket.SendActMsg(CM_LONGHIT, lpptPos.getX(), lpptPos.getY(), bDir);
                        }
                        else if ( m_bFireHitCnt == 1 && m_stAbility.MP > 7 )
                        {
                            SetMotionFrame(_MT_ONEVSWING, bDir);
                            LoadEffect(PlayScene.m_xImage, _SKILL_FIRESWORD, bDir);
                            m_bUseSwordEffect = true;
                            MySocket.SendActMsg(CM_FIREHIT, lpptPos.getX(), lpptPos.getY(), bDir);
                            m_bFireHitCnt = 2;
                        }
                        else if ( m_bYedoCnt == 1 )
                        {
                            SetMotionFrame(_MT_ONEVSWING, bDir);
                            LoadEffect(PlayScene.m_xImage, _SKILL_YEDO, bDir);
                            m_bUseSwordEffect = true;
                            MySocket.SendActMsg(CM_POWERHIT, lpptPos.getX(), lpptPos.getY(), bDir);
                            m_bYedoCnt = 2;
                        }
                        else if ( m_bUseBanwol && m_stAbility.MP > 3 )
                        {
                            SetMotionFrame(_MT_ONEHSWING, bDir);
                            LoadEffect(PlayScene.m_xImage, _SKILL_BANWOL, bDir);
                            m_bUseSwordEffect = true;
                            MySocket.SendActMsg(CM_WIDEHIT, lpptPos.getX(), lpptPos.getY(), bDir);
                        }
                        else
                        {
                            SetMotionFrame(m_bAttackMode, bDir);
                            if ( m_bAttackMode == _MT_WHEELWIND )
                            {
                                LoadEffect(PlayScene.m_xImage, _SKILL_JUMPSHOT, bDir);
                                m_bUseSwordEffect = true;
                            }
                            else if ( m_bAttackMode == _MT_RANDSWING )
                            {
                                LoadEffect(PlayScene.m_xImage, _SKILL_RANDSWING, bDir);
                                m_bUseSwordEffect = true;
                            }
                            ushort wAttackStyle;
                            if ( m_bAttackMode != _MT_ONEVSWING && m_bAttackMode != _MT_ONEHSWING )
                                wAttackStyle = _MT_ONEVSWING;
                            else
                            {
                                wAttackStyle = m_bAttackMode;
                            }
                            MySocket.SendHitMsg(CM_HIT, lpptPos.getX(), lpptPos.getY(), bDir, wAttackStyle);
                        }
                    }
                    break;
                }
                case _MT_MOODEPO:
                {
                    if ( lpptPos !is null )      
                    {
                        MySocket.SendSpellMsg(skill, lpptPos.getX(), lpptPos.getY(), 0);
                        // ¹«ÅÂº¸ ½Ã°£°ª ±â·Ï.
                        m_dwLastRushTime = Clock.currSystemTick();
                        m_bMotionLock = m_bInputLock = true;
                        m_bWarMode  = true;
                    }
                    
                    break;
                }
                case _MT_SPELL2:
                case _MT_SPELL1:
                {
                    if ( lpptPos !is null )
                    {
                        MySocket.SendSpellMsg(skill, lpptPos.getX(), lpptPos.getY(), nMouseTargetID);
                        
                        m_wOldPosX = m_wPosX;
                        m_wOldPosY = m_wPosY;
                        m_bOldDir = m_bCurrDir;
                        
                        m_bMotionLock = m_bInputLock = true;
                        m_bWarMode = true;
                        
                        if ( m_shCurrMagicID == _SKILL_FIREBALL || m_shCurrMagicID == _SKILL_FIREBALL2 || m_shCurrMagicID == _SKILL_FIRE || m_shCurrMagicID == _SKILL_SHOOTLIGHTEN ||
                            m_shCurrMagicID == _SKILL_HANGMAJINBUB || m_shCurrMagicID == _SKILL_DEJIWONHO || m_shCurrMagicID == _SKILL_FIRECHARM || m_shCurrMagicID == _SKILL_SINSU ||
                            m_shCurrMagicID == _SKILL_BIGCLOAK )
                            LoadEffect(PlayScene.m_xImage, m_shCurrMagicID, bDir);
                        else
                            LoadEffect(PlayScene.m_xImage, m_shCurrMagicID);
                        
                        m_bUseEffect = true;
                        
                        /*                  if ( m_shCurrMagicID == _SKILL_SHIELD )
                    {
                        m_dwFstEffectFrame = 50;
                        m_dwEndEffectFrame = 60;
                        m_dwCurrEffectFrame = 50;
                        m_bEffectFrame = 0;
                        m_bEffectFrameCnt = _DEFAULT_SPELLFRAME;
                    }
*/
                        if ( m_shCurrMagicID == _SKILL_SHOWHP )
                            m_bEffectFrameCnt = 20;
                        else if ( m_shCurrMagicID == _SKILL_LIGHTFLOWER )
                            m_bEffectFrameCnt = 15;
                        else if ( m_shCurrMagicID == _SKILL_SPACEMOVE )
                            m_bEffectFrameCnt = 19;
                        else if ( m_shCurrMagicID == _SKILL_LIGHTENING )            
                            m_bEffectFrameCnt = 17;
                        
                        
                        SetMotionFrame(bMtn, bDir);
                    }
                    break;
                }
                case _MT_CUT:
                {
                    m_bInputLock = true;
                    m_bMotionLock = true;
                    m_wOldPosX = m_wPosX;
                    m_wOldPosY = m_wPosY;
                    m_bOldDir = m_bCurrDir;
                    SetMotionFrame(bMtn, bDir);
                    
                    MySocket.SendActMsg(CM_SITDOWN, m_wPosX, m_wPosY, bDir);
                    if ( nMouseTargetID && bIsDead )
                    {
                        MySocket.SendButchAnimal(m_wPosX, m_wPosY, bDir, nMouseTargetID);
                    }
                    break;
                }
                case _MT_HORSESTAND:
                case _MT_STAND:
                {
                    if ( bDir != m_bCurrDir )   
                    {
                        MySocket.SendActMsg(CM_TURN, m_wPosX, m_wPosY, bDir);
                        m_bMotionLock = true;
                        m_bInputLock = true;
                        m_wOldPosX = m_wPosX;
                        m_wOldPosY = m_wPosY;
                        m_bOldDir = m_bCurrDir;
                        SetMotionFrame(bMtn, bDir);
                    }
                    break;
                }
                default: break;
            }
        }
        else
        {
            switch ( bMtn )
            {
                case _MT_HORSESTAND:
                case _MT_STAND:
                {
                    bMtn = _MT_MON_STAND;
                    if ( bDir != m_bCurrDir )
                        MySocket.SendActMsg(CM_TURN, m_wPosX, m_wPosY, bDir);
                    //m_bMotionLock = true;
                    m_wOldPosX = m_wPosX;
                    m_wOldPosY = m_wPosY;
                    m_bOldDir  = m_bCurrDir;
                    SetMotionFrame(bMtn, bDir);
                    break;
                }

                case _MT_WALK:
                case _MT_HORSEWALK:
                {
                    bMtn = _MT_MON_WALK;
                    MySocket.SendActMsg(CM_WALK, m_wPosX, m_wPosY, bDir);
                    m_bMotionLock = m_bInputLock = true;
                    m_wOldPosX = m_wPosX;
                    m_wOldPosY = m_wPosY;
                    m_bOldDir = m_bCurrDir;
                    
                    SetMotionFrame(bMtn, bDir);
                    m_bMoveSpeed = _SPEED_WALK;
                    m_pxMap.ScrollMap(m_bMoveDir, m_dwCurrFrame-m_dwFstFrame, m_bMoveSpeed);
                    break;
                }

                case _MT_ONEHSWING:
                {
                    if ( lpptPos !is null )
                    {
                        bMtn = _MT_MON_ATTACK_A;
                        m_bMotionLock = m_bInputLock = true;
                        m_wOldPosX = m_wPosX;
                        m_wOldPosY = m_wPosY;
                        m_bOldDir = m_bCurrDir;
                        m_bWarMode = true;
                        m_dwWarModeTime = 0;
                        
                        SetMotionFrame(_MT_MON_ATTACK_A, bDir);
                        MySocket.SendHitMsg(CM_HIT, lpptPos.getX(), lpptPos.getY(), bDir, 0);
                    }
                    break;
                }

                default: break;
            }
        }
        
        m_dwMotionLockTime  = 0;
    }

    public void SetMapHandler(MirMap pxMap)
    {
        m_pxMap = pxMap;
    }

    public void SetMagic(ClientMagic* pstMagic, ubyte bKeyNum, ubyte bDir, int nTargetID, Feature stTargetFeature, IntPoint ptTargetPos)
    {
        TickDuration curtime = Clock.currSystemTick();
        if ( !m_bMotionLock && !m_bInputLock )
        {
            switch ( m_shCurrMagicID )
            {
                case _SKILL_MOOTEBO:
                    if ( (curtime - m_dwLastRushTime).msecs > 3000 )
                    {
                        SetMotionState(_MT_MOODEPO, bDir, 0, false, &ptTargetPos, m_shCurrMagicID);
                        //SetMotionState(_MT_MOODEPO, bDir, &ptTargetPos, bKeyNum, 0);
                    }
                    break;
                case _SKILL_HANGMAJINBUB:
                case _SKILL_DEJIWONHO:
                case _SKILL_FIRECHARM:
                case _SKILL_FIRE:
                case _SKILL_FIREBALL2:
                case _SKILL_SINSU:
                case _SKILL_FIREBALL:
                case _SKILL_SHOOTLIGHTEN:
                case _SKILL_BIGCLOAK:
                    m_dwLastSpellTime = curtime;
                    m_wMagicDelayTime = 200 + pstMagic.Def.DelayTime;
                    SetMotionState(_MT_SPELL1, bDir, nTargetID, true, &ptTargetPos, m_shCurrMagicID);
                    break;
                case _SKILL_FIREWIND:   
                case _SKILL_AMYOUNSUL:
                case _SKILL_TAMMING:
                case _SKILL_KILLUNDEAD:
                case _SKILL_HEALLING:
                case _SKILL_HOLYSHIELD:
                case _SKILL_BIGHEALLING:
                case _SKILL_LIGHTFLOWER:
                case _SKILL_SKELLETON:
                case _SKILL_SNOWWIND:
                case _SKILL_SHIELD:
                case _SKILL_SHOWHP:
                case _SKILL_EARTHFIRE:
                case _SKILL_FIREBOOM:
                case _SKILL_SPACEMOVE:
                case _SKILL_CLOAK:
                case _SKILL_LIGHTENING:
                    m_dwLastSpellTime = curtime;
                    m_wMagicDelayTime = 200 + pstMagic.Def.DelayTime;
                    SetMotionState(_MT_SPELL2, bDir, nTargetID, true, &ptTargetPos, m_shCurrMagicID);
                    break;
                default: break;
            }
        }
        
        switch ( m_shCurrMagicID )
        {
            // ¿°È­°á.
            case _SKILL_FIRESWORD:
                if ( (curtime - m_dwLastFireHitTime).msecs > 10000 )
                    MySocket.SendSpellMsg(m_shCurrMagicID, 0, 0, nTargetID);
                break;
                // ¿°È­°áÀÌ¿ÜÀÇ °Ë¹ý.
            case _SKILL_BANWOL:
            case _SKILL_ERGUM:
                if ( (curtime - m_dwLastSpellTime).msecs > 200 )
                {
                    m_dwLastSpellTime = curtime;
                    m_wMagicDelayTime = 0;
                    MySocket.SendSpellMsg(m_shCurrMagicID, 0, 0, nTargetID);
                }
                break;
            default: break;
        }
        
        // °ø°Ý¸¶¹ýÀÏ¶§ Á¢¼ÓÁ¾·á ¸øÇÏ°ÔÇÏ´Â Delay.
        switch ( m_shCurrMagicID )
        {
            case _SKILL_MOOTEBO:    case _SKILL_FIRESWORD:      case _SKILL_BANWOL:     case _SKILL_ERGUM:          case _SKILL_FIRECHARM:
            case _SKILL_FIRE:       case _SKILL_FIREBALL2:      case _SKILL_FIREBALL:   case _SKILL_SHOOTLIGHTEN:   case _SKILL_AMYOUNSUL:
            case _SKILL_KILLUNDEAD: case _SKILL_LIGHTFLOWER:    case _SKILL_SNOWWIND:   case _SKILL_EARTHFIRE:
                m_dwLastMagicTime = curtime;
                break;
            default: break;
        }
        
        // »ç¶÷À» °ø°ÝÇßÀ»¶§ ¿òÁ÷ÀÓ ÆÐ³ÎÆ¼ Delay.
        m_wMagicPKDelayTime = 0;
        if ( nTargetID )
        {
            if ( stTargetFeature.Gender == 0 || stTargetFeature.Gender == 1 )
                m_wMagicPKDelayTime = 300 + randomRange(1, 1100);
        }
    }

    public bool SetOldPosition()
    {
        m_wPosX = m_wOldPosX;
        m_wPosY = m_wOldPosY;
        m_bCurrDir = m_bOldDir;
        
        if ( m_stFeatureEx.Horse == _HORSE_NONE )
        {
            if ( SetMotionFrame(_MT_STAND, m_bCurrDir) )
            {
                AdjustMyPostion();
                m_bMotionLock = m_bInputLock = false;
                return true;
            }
        }
        else
        {
            if ( SetMotionFrame(_MT_HORSESTAND, m_bCurrDir) )
            {
                AdjustMyPostion();
                m_bMotionLock = m_bInputLock = false;
                return true;
            }
        }
        return false;
    }

    public bool CheckMyPostion()
    {
        if ( m_wPosX != m_pxMap.m_shStartViewTileX + _GAPX_TILE_CHAR_MAP ||
            m_wPosY != m_pxMap.m_shStartViewTileY + _GAPY_TILE_CHAR_MAP )
        {
            return false;
        }
        return true;
    }

    public void AdjustMyPostion()
    {
        m_pxMap.SetStartViewTile(m_wPosX-_GAPX_TILE_CHAR_MAP, m_wPosY-_GAPY_TILE_CHAR_MAP);
        m_pxMap.LoadNewMapBuffer();
    }

    // PDS: MirMap contains this..
    public ubyte CalcDirection(int nTargetTileX, int nTargetTileY)
    {
        int nWidth = nTargetTileX - m_wPosX;
        int nHeight = nTargetTileY - m_wPosY;
        
        float rLineLength, rBottomInTriangle;
        int nDimension;
        ubyte bDir;
        rLineLength = std.math.sqrt(cast(double)(nWidth*nWidth+nHeight*nHeight));
        
        // ±âº».
        // 7  0  1          
        // 6     2
        // 5  4  3
        // ÀÏ´ÜÀº 4°³ÀÇ ºÐ¸é(90µµ±âÁØ)À¸·Î ³ª´©°í ºÐ¸é¿¡ ´ëÇÑ cos°ªÀ» Àû¿ëÇÑ´Ù.
        ( nWidth >= 0 ) ?
            ( nHeight <  0 ? (rBottomInTriangle=-nHeight, nDimension=0) : (rBottomInTriangle= nWidth, nDimension=2) ):
            ( nHeight >= 0 ? (rBottomInTriangle= nHeight, nDimension=4) : (rBottomInTriangle=-nWidth, nDimension=6) );
        // 6(cos45)  0(cos 0)  0(cos45)
        // 4(cos90)  2(cos 0)  2(cos 0)
        // 4(cos45)  2(cos90)  2(cos45)
        
        float rCosVal = rBottomInTriangle/rLineLength;
        
        //  cos(0), cos(pi/8), cos(pi/4), cos(pi/2)
        const float[4] rCosVal8 = [ 1.0f, 0.923880f, 0.707107f, 0.382683f ];
        
        // °¢ºÐ¸éÀ» 3°³ÀÇ ¿µ¿ªÀ¸·Î ³ª´©¾î¼­ ¿µ¿ªÀ» ÀçÁ¶Á¤ÇÑ´Ù.
        bDir = 0;
        for ( int nCnt = 0; nCnt < 4; nCnt++ )
        {
            if( rCosVal <= rCosVal8[nCnt] )
                bDir = cast(ubyte)(nDimension+(nCnt+1)/2);
            else break;
        }
        
        if ( bDir >= 8 ) bDir = 0;
        
        return bDir;
    }

    // PDS: MirMap contains this..
    public IntPoint GetPosMouseToTile(int nXPos, int nYPos)
    {
        IntPoint ptTilePos;
        
        if ( m_pxMap !is null )
        {
            ptTilePos.set( m_pxMap.m_shStartViewTileX + (nXPos - _VIEW_CELL_X_START) / _CELL_WIDTH,
                           m_pxMap.m_shStartViewTileY + (nYPos - _VIEW_CELL_Y_START) / _CELL_HEIGHT );
        }
        
        return ptTilePos;
    }
    
    public override bool Create(ImageHandler handler, ubyte bMtn, ubyte bDir, ushort wPosX, ushort wPosY, PFeature pstFeature, PFeatureEx pstFeatureEx)
    {
        m_stAbility = m_stAbility.init;
        m_stSubAbility = m_stSubAbility.init;
        
        if ( !(super.Create(handler, bMtn, bDir, wPosX, wPosY, pstFeature, pstFeatureEx)) )
            return false;
        
        return true;
    }

    public override void OnHealthSpellChanged(PPacketMsg packet)
    {
        m_stAbility.HP = packet.stDefMsg.Param;
        m_stAbility.MaxHP = packet.stDefMsg.Series;
        m_stAbility.MP = packet.stDefMsg.Tag;
        
        if ( m_stAbility.HP <= 0 ) m_stAbility.HP = m_bHPPercent = 0;
        else
        {
            m_bHPPercent = cast(ubyte)get_percentage(m_stAbility.HP, m_stAbility.MaxHP);
        }
    }

    public override bool UpdatePacketState()
    {
        int nLeftMsgCnt;
        PPacketMsg packet = null;

        nLeftMsgCnt = m_xPriorPacketQueue.count();
        
        if ( nLeftMsgCnt > 0 )
        {
            packet = m_xPriorPacketQueue.dequeue();

            if ( packet !is null )
            {
                if ( packet.stDefMsg.Ident == SM_NOWDEATH || packet.stDefMsg.Ident == SM_DEATH )
                {
                    OnDeath(packet);
                    destroy(packet);
                    return true;
                }
            }
        }

        if ( m_bCurrMtn == _MT_STAND || m_bCurrMtn == _MT_ATTACKMODE || m_bCurrMtn == _MT_HORSESTAND || (m_bCurrMtn==_MT_SPELL1 && m_dwCurrFrame==m_dwEndFrame - 2) )
        {
            nLeftMsgCnt = m_xPacketQueue.count();
            
            if ( nLeftMsgCnt > 0 )
            {
                packet = m_xPacketQueue.dequeue();
                
                if ( nLeftMsgCnt >= 3 ) m_bMsgHurryCheck = true;
                else m_bMsgHurryCheck = false;

                if ( packet !is null )
                {
                    switch ( packet.stDefMsg.Ident )
                    {
                        case SM_STRUCK:
                        {
                            m_bInputLock = true;
                            m_stAbility.HP = packet.stDefMsg.Param;
                            m_stAbility.MaxHP = packet.stDefMsg.Tag;
                            OnStruck(packet);

                            TickDuration curtime = Clock.currSystemTick();

                            // PDS:
                            /*ubyte rr, gg, bb, aa;
                            al_unmap_rgba(m_dwNameClr, &rr, &gg, &bb, &aa);
                            if ( rr == 255 )
                            {
                                m_dwLastPKStruckTime = curtime;
                            }*/
                            if ( m_dwNameClr.r == 1.0f )
                            {
                                m_dwLastPKStruckTime = curtime;
                            }
                            m_dwLastStruckTime = curtime;
                            break;
                        }
                        case SM_RUSH:
                        {
                            m_bInputLock = true;
                            m_wOldPosX = m_wPosX;
                            m_wOldPosY = m_wPosY;
                            m_bOldDir = m_bCurrDir;
                            OnRush(packet);
                            if ( !CheckMyPostion() )
                                AdjustMyPostion();
                            m_pxMap.ScrollMap(m_bMoveDir, m_dwCurrFrame-m_dwFstFrame, m_bMoveSpeed);
                            break;
                        }
                        case SM_BACKSTEP:
                        {
                            m_bInputLock = true;
                            m_wOldPosX = m_wPosX;
                            m_wOldPosY = m_wPosY;
                            m_bOldDir = m_bCurrDir;
                            OnBackStep(packet);
                            
                            if ( !CheckMyPostion() )
                                AdjustMyPostion();
                            m_pxMap.ScrollMap(m_bMoveDir, m_dwCurrFrame-m_dwFstFrame, m_bMoveSpeed);           
                            break;
                        }
                        case SM_FEATURECHANGED:     {   OnFeatureChanged(packet);      break;  }
                        case SM_OPENHEALTH:         {   OnOpenHealth(packet);          break;  }
                        case SM_CLOSEHEALTH:        {   OnCloseHealth(packet);         break;  }
                        case SM_CHANGELIGHT:        {   OnChangeLight(packet);         break;  }
                        case SM_USERNAME:           {   OnUserName(packet);            break;  }
                        case SM_CHANGENAMECOLOR:    {   OnChangeNameClr(packet);       break;  }
                        case SM_CHARSTATUSCHANGED:  {   OnCharStatusChanged(packet);   break;  }
                        case SM_MAGICFIRE:          {   OnMagicFire(packet);           break;  }
                        case SM_HEALTHSPELLCHANGED: {   OnHealthSpellChanged(packet);  break;  }
                        default: break;
                    }
                    destroy(packet);
                }
                
                return true;
            }
        }
        return false;
    }

    public override void UpdateMotionState(int nLoopTime, bool bIsMoveTime)
    {
        if ( m_bMotionLock ) 
            m_dwMotionLockTime += nLoopTime;
        
        if ( m_bWarMode ) 
            m_dwWarModeTime += nLoopTime;
        
        m_wABlendCurrDelay += nLoopTime;
        if ( m_wABlendCurrDelay >= m_wABlendDelay )
        {
            m_wABlendCurrDelay = 0;
            m_wABlendDelay = 0;
            m_bABlendRev = false;
        }
        
        if ( m_bCurrMtn == _MT_DIE && m_dwCurrFrame >= m_dwEndFrame-1 )
        {
            m_bIsDead = true;
        }
        
        if ( m_bIsDead )
        {
            if ( m_bIsMon )
                SetMotionFrame(_MT_MON_DIE, m_bCurrDir);
            else
                SetMotionFrame(_MT_DIE, m_bCurrDir);
            
            m_dwCurrFrame = m_dwEndFrame - 1;
            m_bInputLock = true;
            return;
        }

        if ( UpdateMove(bIsMoveTime) )
        {
            UpdatePacketState();
            return;
        }
        else
        {
            if ( !m_bIsMon )
            {
                m_wCurrDelay += nLoopTime;
                
                m_wShieldCurrDelay += nLoopTime;
                
                if ( m_wShieldCurrDelay > 150 )  
                {
                    m_bShieldCurrFrm++;
                    m_wShieldCurrDelay = 0;
                    if ( m_bShieldCurrFrm > 2 )
                        m_bShieldCurrFrm = 0;
                }
                
                if ( m_wCurrDelay > m_wDelay )
                {
                    m_wCurrDelay  = 0;
                    
                    if ( m_dwCurrFrame < m_dwEndFrame )
                    {
                        m_dwCurrFrame++;
                        
                        PlayActSound();
                        
                        if ( (m_bCurrMtn == _MT_SPELL2 || m_bCurrMtn == _MT_SPELL1) && m_bUseEffect )
                        {
                            m_dwCurrEffectFrame++;
                            m_bEffectFrame++;
                        }
                    }
                }
                
                UpdatePacketState();
                
                // ¿¬¼ÓÀûÀÎ ÇÁ·¹ÀÓ Áß¿¡¼­ ÇØ¾ßÇÒÀÏ.
                if ( m_dwMotionLockTime > _MOTION_LOCKTIME )
                {
                    m_dwMotionLockTime = 0;
                    m_bMotionLock = false;
                    //              SetOldPosition();
                }
                
                if ( m_dwWarModeTime > _WARMODE_TIME )
                {
                    m_dwWarModeTime = 0;
                    m_bWarMode = false;
                }
                
                if ( m_dwCurrFrame >= m_dwEndFrame-1 )
                {
                    if ( (m_bCurrMtn == _MT_SPELL2) && m_bUseEffect )
                    {
                        if ( m_dwCurrEffectFrame - m_dwFstEffectFrame < m_bEffectFrameCnt-2 )
                        {
                            m_dwCurrFrame = m_dwEndFrame - 2;
                        }
                    }
                }
                else if ( m_dwCurrFrame >= m_dwEndFrame-3 )
                {
                    if ( (m_bCurrMtn == _MT_SPELL1) && m_bUseEffect )
                    {
                        if ( m_dwCurrEffectFrame - m_dwFstEffectFrame < m_bEffectFrameCnt-5 )
                        {
                            m_dwCurrFrame = m_dwEndFrame - 4;
                        }
                    }
                }
                
                if ( m_dwCurrFrame >= m_dwEndFrame )
                {
                    switch ( m_bCurrMtn )
                    {
                        case _MT_CUT:
                        {
                            m_bInputLock = false;
                            m_bMotionLock = false;
                            m_dwCurrFrame = m_dwFstFrame;
                            if ( m_stFeatureEx.Horse == _HORSE_NONE ) SetMotionFrame(_MT_STAND, m_bCurrDir);
                            else SetMotionFrame(_MT_HORSESTAND, m_bCurrDir);
                        }
                            break;
                        case _MT_STAND:
                        case _MT_HORSESTAND:
                        {
                            m_bInputLock = false;
                            m_bMotionLock = false;
                            m_bUseEffect = false;
                            m_bUseSwordEffect = false;
                            m_dwCurrFrame = m_dwFstFrame;
                            break;
                        }
                        case _MT_ATTACKMODE:
                        {
                            if ( !m_bWarMode )
                                SetMotionFrame(_MT_STAND, m_bCurrDir);
                            else
                            {
                                m_bInputLock = false;
                                m_bMotionLock = false;
                                m_dwCurrFrame = m_dwFstFrame;
                            }
                            break;
                        }
                        default:
                        {
                            m_dwCurrEffectFrame = 0;
                            m_dwFstEffectFrame = 0;
                            m_dwEndEffectFrame = 0;
                            m_bEffectFrame = 0;
                            m_bEffectFrameCnt = _DEFAULT_SPELLFRAME;
                            m_bUseEffect = false;
                            m_bUseSwordEffect = false;
                            if ( !m_bMotionLock )
                            {               
                                m_bInputLock = false;
                                
                                if ( m_stFeatureEx.Horse == _HORSE_NONE )
                                {
                                    if ( m_bWarMode )
                                        SetMotionFrame(_MT_ATTACKMODE, m_bCurrDir);
                                    else
                                        SetMotionFrame(_MT_STAND, m_bCurrDir);
                                }
                                else
                                {
                                    SetMotionFrame(_MT_HORSESTAND, m_bCurrDir);
                                }
                            }
                            else
                            {
                                m_dwCurrFrame = m_dwEndFrame-1;
                            }
                        }
                            break;
                    }
                }
                
                if ( m_bCurrMtn == _MT_STAND || m_bCurrMtn == _MT_HORSESTAND )
                {
                    // TODO:
                    /+POINT ptMouse;
                    GetCursorPos(&ptMouse);
                    ScreenToClient(g_xMainWnd.GetSafehWnd(), &ptMouse);
                    m_bCanRun = false;
                    
                    if ( HIBYTE(GetKeyState(VK_RBUTTON)) || ( HIBYTE(GetKeyState(VK_LBUTTON)) && HIBYTE(GetKeyState(VK_CONTROL))) )
                    {
                        OnRButtonDown(ptMouse);
                    }
                    else if ( HIBYTE(GetKeyState(VK_LBUTTON)) )
                    {
                        OnLButtonDown(ptMouse);
                        
/*                  LPARAM lParam = MAKELPARAM((WORD)ptMouse.x, (WORD)ptMouse.y);
                    WPARAM wParam = 0;
                    g_xGameProc.OnLButtonDown(wParam, lParam);
*/              }+/
                }
            }
            else
            {
                m_wCurrDelay += nLoopTime;
                m_wShieldCurrDelay += nLoopTime;
                m_wABlendCurrDelay += nLoopTime;
                
                if ( m_wShieldCurrDelay > 150 )  
                {
                    m_bShieldCurrFrm++;
                    m_wShieldCurrDelay = 0;
                    if ( m_bShieldCurrFrm > 2 )
                        m_bShieldCurrFrm = 0;
                }
                
                if ( m_wCurrDelay > m_wDelay )
                {
                    m_wCurrDelay  = 0;
                    
                    if ( m_dwCurrFrame < m_dwEndFrame )
                    {
                        m_dwCurrFrame++;
                    }
                }
                
                UpdatePacketState();
                
                // ¿¬¼ÓÀûÀÎ ÇÁ·¹ÀÓ Áß¿¡¼­ ÇØ¾ßÇÒÀÏ.
                if ( m_dwMotionLockTime > _MOTION_LOCKTIME )
                {
                    m_dwMotionLockTime = 0;
                    m_bMotionLock = false;
                    //              SetOldPosition();
                }
                
                if ( m_dwWarModeTime > _WARMODE_TIME )
                {
                    m_dwWarModeTime = 0;
                    m_bWarMode = false;
                }
                
                
                if ( m_dwCurrFrame >= m_dwEndFrame )
                {
                    switch ( m_bCurrMtn )
                    {
                        case _MT_MON_STAND:
                        {
                            m_bInputLock = false;
                            m_bMotionLock = false;
                            m_bUseEffect = false;
                            m_bUseSwordEffect = false;
                            m_dwCurrFrame = m_dwFstFrame;
                            break;
                        }
                        default:
                        {
                            m_dwCurrEffectFrame = 0;
                            m_dwFstEffectFrame = 0;
                            m_dwEndEffectFrame = 0;
                            m_bEffectFrame = 0;
                            m_bEffectFrameCnt = _DEFAULT_SPELLFRAME;
                            m_bUseEffect = false;
                            m_bUseSwordEffect = false;
                            if ( !m_bMotionLock )
                            {               
                                m_bInputLock = false;
                                SetMotionFrame(_MT_MON_STAND, m_bCurrDir);
                            }
                            else
                            {
                                m_dwCurrFrame = m_dwEndFrame-1;
                            }
                        }
                            break;
                    }
                }
                
                if ( m_bCurrMtn == _MT_MON_STAND )
                {
                    // TODO:
                    /+POINT ptMouse;
                    GetCursorPos(&ptMouse);
                    ScreenToClient(g_xMainWnd.GetSafehWnd(), &ptMouse);
                    m_bCanRun = false;
                    
                    if ( HIBYTE(GetKeyState(VK_RBUTTON)) || ( HIBYTE(GetKeyState(VK_LBUTTON)) && HIBYTE(GetKeyState(VK_CONTROL))) )
                    {
                        OnRButtonDown(ptMouse);
                    }
                    else if ( HIBYTE(GetKeyState(VK_LBUTTON)) )
                    {
                        OnLButtonDown(ptMouse);
/*                  LPARAM lParam = MAKELPARAM((WORD)ptMouse.x, (WORD)ptMouse.y);
                    WPARAM wParam = 0;
                    g_xGameProc.OnLButtonDown(wParam, lParam);
*/              }+/
                }
            }
        }
    }

    public override bool UpdateMove(bool bIsMoveTime)
    {
        void shift()
        {
            m_bIsMoved = false;
            m_bInputLock = false;
            m_pxMap.SetMovedTileBuffer(m_shShiftTileX, m_shShiftTileY);
            m_bMoveSpeed = 0;
            m_shShiftPixelX = 0;
            m_shShiftPixelY = 0;
            m_shShiftTileX = 0;
            m_shShiftTileY = 0;
        }

        if ( !m_bIsMon )
        {
            if ( m_bCurrMtn == _MT_WALK || m_bCurrMtn == _MT_RUN || m_bCurrMtn == _MT_HORSEWALK || m_bCurrMtn == _MT_HORSERUN || m_bCurrMtn == _MT_MOODEPO || m_bCurrMtn == _MT_PUSHBACK)
            {
                m_wCurrDelay = 0;
                
                if ( bIsMoveTime )
                {
                    if ( m_bCurrMtn == _MT_PUSHBACK )
                    {
                        m_bBackStepFrame += 2;
                        if ( m_bBackStepFrame >= m_bBackStepFrameCnt )
                        {
                            m_dwCurrFrame++;
                            m_bBackStepFrame = m_bBackStepFrameCnt;
                        }
                    }
                    else if ( m_bCurrMtn == _MT_MOODEPO )
                    {
                        m_dwCurrFrame += 2;
                        if ( m_dwCurrFrame >= m_dwEndFrame )
                        {
                            m_dwCurrFrame = m_dwEndFrame;
                        }
                    }
                    else
                    {
                        m_dwCurrFrame++;
                        
                        PlayMoveSound();
                        /*                  if ( m_bMsgHurryCheck )         
                    {
                        m_dwCurrFrame++;
                    }
*/              }
                    
                    if ( m_bCurrMtn == _MT_PUSHBACK )
                    {
                        if ( m_bBackStepFrame >= m_bBackStepFrameCnt-m_bMoveNextFrmCnt && !m_bIsMoved )
                        {
                            SetMoved();
                            m_bIsMoved = true;
                        }
                    }
                    else
                    {
                        if ( m_dwCurrFrame >= m_dwEndFrame-m_bMoveNextFrmCnt && !m_bIsMoved )
                        {
                            SetMoved();
                            m_bIsMoved = true;
                        }
                    }
                }
                
                // ¿¬¼ÓÀûÀÎ ÇÁ·¹ÀÓ Áß¿¡¼­ ÇØ¾ßÇÒÀÏ.
                if ( m_dwCurrFrame >= m_dwEndFrame )
                {
                    m_dwCurrFrame = m_dwEndFrame-1;
                    m_bCanRun = false;
                    
                    switch ( m_bCurrMtn )
                    {
                        case _MT_MOODEPO:
                        {
                            if ( m_bInputLock )
                            {
                                shift();

                                // ÀÌµ¿ÈÄ ÁÂÇ¥È®ÀÎ.
                                if ( CheckMyPostion() == false )
                                    AdjustMyPostion();
                                
                                if ( m_bWarMode )
                                    SetMotionFrame(_MT_ATTACKMODE, m_bCurrDir);
                                else
                                    SetMotionFrame(_MT_STAND, m_bCurrDir);
                            }
                            break;
                        }
                        case _MT_PUSHBACK:
                        {
                            if ( m_bInputLock )
                            {
                                shift();
                                
                                m_bBackStepFrame = 0;
                                m_bBackStepFrameCnt = 0;
                                // ÀÌµ¿ÈÄ ÁÂÇ¥È®ÀÎ.
                                if ( CheckMyPostion() == false )
                                    AdjustMyPostion();
                                
                                if ( m_bWarMode )
                                    SetMotionFrame(_MT_ATTACKMODE, m_bCurrDir);
                                else
                                    SetMotionFrame(_MT_STAND, m_bCurrDir);
                            }
                            break;
                        }
                        case _MT_WALK:
                        case _MT_HORSEWALK:
                        {
                            if ( !m_bMotionLock && m_bInputLock )
                            {
                                shift();
                                
                                if ( m_bCurrMtn == _MT_WALK && m_stFeatureEx.Horse == _HORSE_NONE )
                                {
                                    if ( m_bWarMode )
                                    {
                                        SetMotionFrame(_MT_ATTACKMODE, m_bCurrDir);
                                    }
                                    else SetMotionFrame(_MT_STAND, m_bCurrDir);
                                }
                                else SetMotionFrame(_MT_HORSESTAND, m_bCurrDir);
                                
                                // ÀÌµ¿ÈÄ ÁÂÇ¥È®ÀÎ.
                                if ( CheckMyPostion() == false )
                                    AdjustMyPostion();

                                // TODO:
                                /*POINT ptMouse;
                                GetCursorPos(&ptMouse);
                                ScreenToClient(g_xMainWnd.GetSafehWnd(), &ptMouse);
                                if ( HIBYTE(GetKeyState(VK_RBUTTON)) || ( HIBYTE(GetKeyState(VK_LBUTTON)) && HIBYTE(GetKeyState(VK_CONTROL))) )
                                {
                                    m_bCanRun = true;
                                    OnRButtonDown(ptMouse);
                                }
                                else if ( HIBYTE(GetKeyState(VK_LBUTTON)) )
                                {
                                    OnLButtonDown(ptMouse);
                                }*/
                            }
                            break;
                        }
                        case _MT_RUN:
                        case _MT_HORSERUN:
                        {
                            if ( !m_bMotionLock && m_bInputLock )
                            {               
                                shift();
                                
                                if ( m_bCurrMtn == _MT_RUN && m_stFeatureEx.Horse == _HORSE_NONE )
                                {
                                    if ( m_bWarMode )
                                    {
                                        SetMotionFrame(_MT_ATTACKMODE, m_bCurrDir);
                                    }
                                    else SetMotionFrame(_MT_STAND, m_bCurrDir);
                                }
                                else SetMotionFrame(_MT_HORSESTAND, m_bCurrDir);
                                
                                if ( CheckMyPostion() == false )
                                    AdjustMyPostion();

                                // TODO:
                                /*POINT ptMouse;
                                GetCursorPos(&ptMouse);
                                ScreenToClient(g_xMainWnd.GetSafehWnd(), &ptMouse);
                                
                                if ( HIBYTE(GetKeyState(VK_RBUTTON)) || ( HIBYTE(GetKeyState(VK_LBUTTON)) && HIBYTE(GetKeyState(VK_CONTROL))) )
                                {
                                    m_bCanRun = true;
                                    OnRButtonDown(ptMouse);
                                }
                                else if ( HIBYTE(GetKeyState(VK_LBUTTON)) )
                                {
                                    OnLButtonDown(ptMouse);
                                }*/
                            }
                            break;
                        }
                        default: break;
                    }
                }
                else
                {
                    switch ( m_bCurrMtn )
                    {
                        case _MT_WALK:
                        case _MT_RUN:
                        case _MT_HORSEWALK:
                        case _MT_HORSERUN:
                        case _MT_MOODEPO:
                            m_pxMap.ScrollMap(m_bMoveDir, m_dwCurrFrame-m_dwFstFrame, m_bMoveSpeed);           
                            // m_pxMap.ScrollMap(m_bMoveDir, m_wCurrDelay, m_wDelay*(m_dwEndFrame-m_dwFstFrame), m_bMoveSpeed);
                            break;
                        case _MT_PUSHBACK:
                            m_pxMap.ScrollMap(m_bMoveDir, m_bBackStepFrame, m_bMoveSpeed);         
                            break;
                        default: break;
                    }
                }
                
                // ¿¬¼ÓÀûÀÎ ÇÁ·¹ÀÓ Áß¿¡¼­ ÇØ¾ßÇÒÀÏ.
                if ( m_dwMotionLockTime > _MOTION_LOCKTIME )
                {
                    m_dwMotionLockTime = 0;
                    m_bMotionLock = false;
                    //SetOldPosition();
                }
                
                if ( m_dwWarModeTime > _WARMODE_TIME )
                {
                    m_dwWarModeTime = 0;
                    m_bWarMode = false;
                }
                
                return true;
            }
        }
        else
        {
            if ( m_bCurrMtn == _MT_MON_WALK )
            {
                m_wCurrDelay = 0;
                
                if ( bIsMoveTime )
                {
                    m_dwCurrFrame++;
                    
                    if ( m_dwCurrFrame >= m_dwEndFrame-m_bMoveNextFrmCnt && !m_bIsMoved )
                    {
                        SetMoved();
                        m_bIsMoved = true;
                    }
                }
                
                if ( m_dwCurrFrame >= m_dwEndFrame )
                {
                    m_dwCurrFrame = m_dwEndFrame-1;
                    m_bCanRun = false;
                    switch ( m_bCurrMtn )
                    {
                        case _MT_MON_WALK:
                        {
                            if ( !m_bMotionLock && m_bInputLock )
                            {
                                shift();
                                
                                SetMotionFrame(_MT_MON_STAND, m_bCurrDir);
                                
                                // ÀÌµ¿ÈÄ ÁÂÇ¥È®ÀÎ.
                                if ( CheckMyPostion() == false )
                                    AdjustMyPostion();

                                // TODO:
                                /*POINT ptMouse;
                                GetCursorPos(&ptMouse);
                                ScreenToClient(g_xMainWnd.GetSafehWnd(), &ptMouse);
                                if ( HIBYTE(GetKeyState(VK_LBUTTON)) )
                                {
                                    OnLButtonDown(ptMouse);
                                }*/
                            }
                            break;
                        }
                        default: break;
                    }
                }
                else
                {
                    switch ( m_bCurrMtn )
                    {
                        case _MT_MON_WALK:
                            m_pxMap.ScrollMap(m_bMoveDir, m_dwCurrFrame-m_dwFstFrame, m_bMoveSpeed);           
                            break;
                        default: break;
                    }
                }
                
                // ¿¬¼ÓÀûÀÎ ÇÁ·¹ÀÓ Áß¿¡¼­ ÇØ¾ßÇÒÀÏ.
                if ( m_dwMotionLockTime > _MOTION_LOCKTIME )
                {
                    m_dwMotionLockTime = 0;
                    m_bMotionLock = false;
                    //SetOldPosition();
                }
                
                if ( m_dwWarModeTime > _WARMODE_TIME )
                {
                    m_dwWarModeTime = 0;
                    m_bWarMode = false;
                }
                
                return true;
            }
        }
        
        return false;
    }

    public bool DrawActor(bool bFocused = false, bool bShadowAblended = false, 
                                   bool bUseScrnPos = false, bool bDrawShadow = true)
    {
        // Ä³¸¯ÅÍ ÁÂÇ¥´Â °íÁ¤.
        m_shScrnPosX = _CHAR_CENTER_XPOS - 24; 
        m_shScrnPosY = _CHAR_CENTER_YPOS - 16;
        
        if ( m_bIsMon )
        {
            if ( Actor.DrawActor(m_pxMap, bFocused, bShadowAblended, bUseScrnPos, bDrawShadow) )
                return true;
        }
        else
        {
            if ( Hero.DrawActor(m_pxMap, bFocused, bShadowAblended, bUseScrnPos, bDrawShadow) )
                return true;
        }
        
        return false;
    }
    
    public override bool ChangeFeature(Feature stFeature, FeatureEx stFeatureEx)
    {
        if ( m_bIsMon )     
        {
            if ( Actor.ChangeFeature(stFeature) )
                return true;
        }
        else    
        {
            if ( Hero.ChangeFeature(stFeature, stFeatureEx) )
                return true;
        }
        
        return false;
    }
    
    //---------------------------------------------------------------------------------------------------------------//
    //  Ä³¸¯ÅÍ Delay°ü·Ã ÇÔ¼ö.
    public bool CanNextHit()
    {
        TickDuration curtime;
        int nNextHit, nLevelFast;
        bool bAttackSlow = false;
        
        nLevelFast = min(370, m_stAbility.Level*14);
        nLevelFast = min(800, nLevelFast + m_shHitSpeed * 60);
        
        if ( m_stAbility.HandWeight > m_stAbility.MaxHandWeight )     
            bAttackSlow = true;
        
        if ( bAttackSlow ) nNextHit = 1400 - nLevelFast + 1500;
        else nNextHit = 1400 - nLevelFast;
        
        curtime = Clock.currSystemTick();
        
        if ( (curtime - m_dwLastHitTime).msecs > nNextHit )
        {
            m_dwLastHitTime = curtime;
            return true;
        }
        
        return false;
    }

    public bool CanWalk()
    {
        TickDuration curtime = Clock.currSystemTick();

        if ( (curtime - m_dwLastSpellTime).msecs >= m_wMagicPKDelayTime )
            return false;
        
        return true;
    }

    public bool CanRun()
    {
        TickDuration curtime = Clock.currSystemTick();

        if ( m_stAbility.HP < _RUN_MINHEALTH )
            return false;
        
        if ( ((curtime - m_dwLastPKStruckTime).msecs < 3000) || 
             ((curtime - m_dwLastSpellTime).msecs < m_wMagicPKDelayTime ) )
            return false;
        
        return true;
    }

    //---------------------------------------------------------------------------------------------------------------//
    //---------------------------------------------------------------------------------------------------------------//
    //  À¯Àú ÀÔ·ÂÇÔ¼ö.
    //public bool OnLButtonDown(POINT ptMouse, INT nTargetID = -1, BOOL bIsDead = FALSE, POINT* lpptTargetPos = NULL);
    //public bool OnRButtonDown(POINT ptMouse);
    //public bool OnKeyDown(WPARAM wParam, LPARAM lParam, POINT ptMouse, POINT ptTargetPos, INT nMosueTargetID, FEATURE stTargetFeature);
}